aboutsummaryrefslogtreecommitdiff
path: root/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip
diff options
context:
space:
mode:
authorTrygve Laugstøl <trygvis@inamo.no>2018-08-23 17:08:59 +0200
committerTrygve Laugstøl <trygvis@inamo.no>2018-08-23 17:12:21 +0200
commit3061ecca3d0fdfb87dabbf5f63c9e06c2a30f53a (patch)
treeab49cc16ed0b853452c5c2ed2d3042416d628986 /thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip
downloadiot-sensors-master.tar.gz
iot-sensors-master.tar.bz2
iot-sensors-master.tar.xz
iot-sensors-master.zip
o Initial import.HEADmaster
Diffstat (limited to 'thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip')
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/CHANGELOG4276
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/COPYING33
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/FILES5
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/README100
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/UPGRADING235
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/doc/FILES9
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/doc/NO_SYS_SampleCode.c117
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/doc/contrib.txt58
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/doc/doxygen/generate.bat1
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/doc/doxygen/generate.sh3
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/doc/doxygen/lwip.Doxyfile2505
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/doc/doxygen/main_page.h132
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/doc/doxygen/output/index.html10
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/doc/mdns.txt113
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/doc/mqtt_client.txt162
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/doc/ppp.txt529
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/doc/rawapi.txt499
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/doc/savannah.txt120
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/doc/snmp_agent.txt181
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/doc/sys_arch.txt303
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/license.txt25
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/FILES15
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/Filelists.mk181
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/api/api_lib.c1010
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/api/api_msg.c1947
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/api/err.c115
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/api/netbuf.c246
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/api/netdb.c413
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/api/netifapi.c221
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/api/pppapi.c416
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/api/sockets.c2827
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/api/tcpip.c518
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/httpd/fs.c179
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/httpd/fs/404.html21
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/httpd/fs/img/sics.gifbin0 -> 724 bytes
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/httpd/fs/index.html47
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/httpd/fsdata.c298
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/httpd/fsdata.h50
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/httpd/httpd.c2629
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/httpd/httpd_structs.h114
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/httpd/makefsdata/makefsdata97
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/httpd/makefsdata/makefsdata.c1033
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/httpd/makefsdata/readme.txt13
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/lwiperf/lwiperf.c661
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/mdns/mdns.c2028
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/mqtt/mqtt.c1341
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/netbiosns/netbiosns.c367
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_asn1.c749
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_asn1.h108
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_core.c1349
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_core_priv.h76
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_mib2.c116
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_mib2_icmp.c182
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_mib2_interfaces.c375
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_mib2_ip.c743
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_mib2_snmp.c227
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_mib2_system.c377
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_mib2_tcp.c594
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_mib2_udp.c357
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_msg.c1668
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_msg.h194
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_netconn.c121
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_pbuf_stream.c156
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_pbuf_stream.h73
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_raw.c100
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_scalar.c220
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_table.c343
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_threadsync.c219
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_traps.c445
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmpv3.c136
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmpv3_dummy.c145
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmpv3_mbedtls.c331
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmpv3_priv.h66
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/sntp/sntp.c727
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/tftp/tftp_server.c417
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/def.c222
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/dhcp.c1812
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/dns.c1573
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/inet_chksum.c609
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/init.c385
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/ip.c124
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/ipv4/autoip.c527
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/ipv4/dhcp.c1950
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/ipv4/etharp.c1206
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/ipv4/icmp.c397
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/ipv4/igmp.c800
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/ipv4/ip4.c1086
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/ipv4/ip4_addr.c331
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/ipv4/ip4_frag.c831
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/ipv4/ip_frag.c881
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/ipv6/README1
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/ipv6/dhcp6.c50
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/ipv6/ethip6.c118
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/ipv6/icmp6.c350
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/ipv6/inet6.c53
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/ipv6/ip6.c1122
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/ipv6/ip6_addr.c292
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/ipv6/ip6_frag.c805
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/ipv6/mld6.c588
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/ipv6/nd6.c2108
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/mem.c777
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/memp.c496
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/netif.c1265
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/pbuf.c1442
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/raw.c521
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/snmp/asn1_dec.c657
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/snmp/asn1_enc.c611
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/snmp/mib2.c4160
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/snmp/mib_structs.c1175
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/snmp/msg_in.c1555
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/snmp/msg_out.c690
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/stats.c169
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/sys.c106
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/tcp.c2164
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/tcp_in.c1818
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/tcp_out.c1671
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/timeouts.c433
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/udp.c1191
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/api.h400
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/api_msg.h211
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/FILES2
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/fs.h103
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/httpd.h236
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/httpd_opts.h323
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/lwiperf.h84
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/mdns.h69
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/mdns_opts.h74
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/mdns_priv.h66
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/mqtt.h244
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/mqtt_opts.h103
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/netbiosns.h43
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/netbiosns_opts.h59
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/snmp.h128
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/snmp_core.h364
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/snmp_mib2.h78
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/snmp_opts.h293
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/snmp_scalar.h113
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/snmp_table.h134
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/snmp_threadsync.h114
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/snmpv3.h90
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/sntp.h76
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/sntp_opts.h173
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/tftp_opts.h105
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/tftp_server.h94
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/arch.h319
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/autoip.h99
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/debug.h167
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/def.h142
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/dhcp.h143
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/dhcp6.h58
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/dns.h130
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/err.h119
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/errno.h193
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/etharp.h106
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/ethip6.h68
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/icmp.h110
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/icmp6.h70
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/igmp.h115
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/inet.h172
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/inet6.h92
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/inet_chksum.h105
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/init.h100
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/ip.h321
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/ip4.h111
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/ip4_addr.h227
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/ip4_frag.h100
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/ip6.h93
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/ip6_addr.h285
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/ip6_frag.h120
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/ip_addr.h407
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/ip_frag.h91
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/mem.h82
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/memp.h153
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/memp_std.h157
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/mld6.h99
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/nd6.h84
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/netbuf.h118
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/netdb.h150
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/netif.h474
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/netifapi.h140
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/opt.h2876
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/pbuf.h263
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/pppapi.h167
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/priv/api_msg.h216
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/priv/memp_priv.h183
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/priv/memp_std.h146
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/priv/nd6_priv.h144
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/priv/tcp_priv.h507
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/priv/tcpip_priv.h160
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/prot/autoip.h78
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/prot/dhcp.h183
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/prot/dns.h140
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/prot/etharp.h91
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/prot/ethernet.h170
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/prot/icmp.h91
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/prot/icmp6.h170
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/prot/igmp.h90
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/prot/ip.h51
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/prot/ip4.h127
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/prot/ip6.h169
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/prot/mld6.h70
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/prot/nd6.h277
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/prot/tcp.h97
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/prot/udp.h68
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/raw.h118
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/sio.h142
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/snmp.h213
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/snmp_asn1.h101
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/snmp_msg.h321
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/snmp_structs.h268
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/sockets.h593
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/stats.h491
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/sys.h455
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/tcp.h435
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/tcpip.h106
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/timeouts.h121
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/timers.h101
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/udp.h182
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/etharp.h3
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ethernet.h77
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/lowpan6.h86
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/lowpan6_opts.h70
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/ccp.h156
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/chap-md5.h36
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/chap-new.h192
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/chap_ms.h44
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/eap.h169
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/ecp.h50
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/eui64.h94
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/fsm.h175
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/ipcp.h126
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/ipv6cp.h183
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/lcp.h171
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/magic.h122
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/mppe.h173
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/polarssl/arc4.h81
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/polarssl/des.h92
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/polarssl/md4.h97
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/polarssl/md5.h96
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/polarssl/sha1.h96
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/ppp.h690
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/ppp_impl.h629
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/ppp_opts.h593
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/pppapi.h137
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/pppcrypt.h136
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/pppdebug.h80
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/pppoe.h179
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/pppol2tp.h201
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/pppos.h118
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/upap.h123
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/vj.h161
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/slipif.h87
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/posix/errno.h33
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/posix/netdb.h33
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/posix/sys/socket.h33
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/FILES24
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/etharp.c1531
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ethernet.c314
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ethernetif.c335
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/lowpan6.c1193
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/PPPD_FOLLOWUP473
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/auth.c2510
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/ccp.c1740
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/chap-md5.c126
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/chap-new.c677
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/chap_ms.c962
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/demand.c465
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/eap.c2423
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/ecp.c191
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/eui64.c56
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/fsm.c799
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/ipcp.c2418
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/ipv6cp.c1533
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/lcp.c2790
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/magic.c294
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/mppe.c412
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/multilink.c609
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/polarssl/README22
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/polarssl/arc4.c101
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/polarssl/des.c422
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/polarssl/md4.c281
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/polarssl/md5.c300
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/polarssl/sha1.c335
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/ppp.c1647
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/pppapi.c427
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/pppcrypt.c66
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/pppoe.c1191
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/pppol2tp.c1131
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/pppos.c875
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/upap.c677
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/utils.c959
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/vj.c695
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/slipif.c555
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/port/arch/cc.h85
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/port/arch/lwipopts.h157
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/port/arch/perf.h36
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/port/arch/sys_arch.h45
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/port/nrf_platform_port.c352
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/port/nrf_platform_port.h47
299 files changed, 133322 insertions, 0 deletions
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/CHANGELOG b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/CHANGELOG
new file mode 100644
index 0000000..3a27719
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/CHANGELOG
@@ -0,0 +1,4276 @@
+HISTORY
+
+(git master)
+
+ * [Enter new changes just after this line - do not remove this line]
+
+(STABLE-2.0.2)
+
+ ++ New features:
+
+ 2017-02-10: Dirk Ziegelmeier
+ * Implement task #14367: Hooks need a better place to be defined:
+ We now have a #define for a header file name that is #included in every .c
+ file that provides hooks.
+
+ ++ Bugfixes:
+
+ 2017-03-08
+ * tcp: do not keep sending SYNs when getting ACKs
+
+ 2017-03-08: Joel Cunningham
+ * tcp: Initialize ssthresh to TCP_SND_BUF (bug #50476)
+
+ 2017-03-01: Simon Goldschmidt
+ * httpd: LWIP_HTTPD_POST_MANUAL_WND: fixed double-free when httpd_post_data_recved
+ is called nested from httpd_post_receive_data() (bug #50424)
+
+ 2017-02-28: David van Moolenbroek/Simon Goldschmidt
+ * tcp: fixed bug #50418: LWIP_EVENT_API: fix invalid calbacks for SYN_RCVD pcb
+
+ 2017-02-17: Simon Goldschmidt
+ * dns: Improved DNS_LOCAL_HOSTLIST interface (bug #50325)
+
+ 2017-02-16: Simon Goldschmidt
+ * LWIP_NETCONN_FULLDUPLEX: fixed shutdown during write (bug #50274)
+
+ 2017-02-13: Simon Goldschmidt/Dirk Ziegelmeier
+ * For tiny targtes, LWIP_RAND is optional (fix compile time checks)
+
+ 2017-02-10: Simon Goldschmidt
+ * tcp: Fixed bug #47485 (tcp_close() should not fail on memory error) by retrying
+ to send FIN from tcp_fasttmr
+
+ 2017-02-09: Simon Goldschmidt
+ * sockets: Fixed bug #44032 (LWIP_NETCONN_FULLDUPLEX: select might work on
+ invalid/reused socket) by not allowing to reallocate a socket that has
+ "select_waiting != 0"
+
+ 2017-02-09: Simon Goldschmidt
+ * httpd: Fixed bug #50059 (httpd LWIP_HTTPD_SUPPORT_11_KEEPALIVE vs.
+ LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED)
+
+ 2017-02-08: Dirk Ziegelmeier
+ * Rename "IPv6 mapped IPv4 addresses" to their correct name from RFC4191:
+ "IPv4-mapped IPv6 address"
+
+ 2017-02-08: Luc Revardel
+ * mld6.c: Fix bug #50220 (mld6_leavegroup does not send ICMP6_TYPE_MLD, even
+ if last reporter)
+
+ 2017-02-08: David van Moolenbroek
+ * ip6.c: Patch #9250: fix source substitution in ip6_output_if()
+
+ 2017-02-08: Simon Goldschmidt
+ * tcp_out.c: Fixed bug #50090 (last_unsent->oversize_left can become wrong value
+ in tcp_write error path)
+
+ 2017-02-02: Dirk Ziegelmeier
+ * Fix bug #50206: UDP Netconn bind to IP6_ADDR_ANY fails
+
+ 2017-01-18: Dirk Ziegelmeier
+ * Fix zero-copy RX, see bug bug #50064. PBUF_REFs were not supported as ARP requests.
+
+ 2017-01-15: Axel Lin, Dirk Ziegelmeier
+ * minor bug fixes in mqtt
+
+ 2017-01-11: Knut Andre Tidemann
+ * sockets/netconn: fix broken default ICMPv6 handling of checksums
+
+(STABLE-2.0.1)
+
+ ++ New features:
+
+ 2016-12-31: Simon Goldschmidt
+ * tcp.h/.c: added function tcp_listen_with_backlog_and_err() to get the error
+ reason when listening fails (bug #49861)
+
+ 2016-12-20: Erik Andersen
+ * Add MQTT client
+
+ 2016-12-14: Jan Breuer:
+ * opt.h, ndc.h/.c: add support for RDNSS option (as per RFC 6106)
+
+ 2016-12-14: David van Moolenbroek
+ * opt.h, nd6.c: Added LWIP_HOOK_ND6_GET_GW()
+
+ 2016-12-09: Dirk Ziegelmeier
+ * ip6_frag.c: Implemented support for LWIP_NETIF_TX_SINGLE_PBUF
+
+ 2016-12-09: Simon Goldschmidt
+ * dns.c: added one-shot multicast DNS queries
+
+ 2016-11-24: Ambroz Bizjak, David van Moolenbroek
+ * tcp_out.c: Optimize passing contiguous nocopy buffers to tcp_write (bug #46290)
+
+ 2016-11-16: Dirk Ziegelmeier
+ * sockets.c: added support for IPv6 mapped IPv4 addresses
+
+ ++ Bugfixes:
+
+ 2016-12-16: Thomas Mueller
+ * api_lib.c: fixed race condition in return value of netconn_gethostbyname()
+ (and thus also lwip_gethostbyname/_r() and lwip_getaddrinfo())
+
+ 2016-12-15: David van Moolenbroek
+ * opt.h, tcp: added LWIP_HOOK_TCP_ISN() to implement less predictable initial
+ sequence numbers (see contrib/addons/tcp_isn for an example implementation)
+
+ 2016-12-05: Dirk Ziegelmeier
+ * fixed compiling with IPv4 disabled (IPv6 only case)
+
+ 2016-11-28: Simon Goldschmidt
+ * api_lib.c: fixed bug #49725 (send-timeout: netconn_write() can return
+ ERR_OK without all bytes being written)
+
+ 2016-11-28: Ambroz Bizjak
+ * tcpi_in.c: fixed bug #49717 (window size in received SYN and SYN-ACK
+ assumed scaled)
+
+ 2016-11-25: Simon Goldschmidt
+ * dhcp.c: fixed bug #49676 (Possible endless loop when parsing dhcp options)
+
+ 2016-11-23: Dirk Ziegelmeier
+ * udp.c: fixed bug #49662: multicast traffic is now only received on a UDP PCB
+ (and therefore on a UDP socket/netconn) when the PCB is bound to IP_ADDR_ANY
+
+ 2016-11-16: Dirk Ziegelmeier
+ * *: Fixed dual-stack behaviour, IPv6 mapped IPv4 support in socket API
+
+ 2016-11-14: Joel Cunningham
+ * tcp_out.c: fixed bug #49533 (start persist timer when unsent seg can't fit
+ in window)
+
+ 2016-11-16: Roberto Barbieri Carrera
+ * autoip.c: fixed bug #49610 (sometimes AutoIP fails to reuse the same address)
+
+ 2016-11-11: Dirk Ziegelmeier
+ * sockets.c: fixed bug #49578 (dropping multicast membership does not work
+ with LWIP_SOCKET_OFFSET)
+
+(STABLE-2.0.0)
+
+ ++ New features:
+
+ 2016-07-27: Simon Goldschmidt
+ * opt.h, timeouts.h/.c: added LWIP_TIMERS_CUSTOM to override the default
+ implementation of timeouts
+
+ 2016-07-xx: Dirk Ziegelmeier
+ * Large overhaul of doxygen documentation
+
+ 2016-04-05: Simon Goldschmidt
+ * timers.h/.c: prepare for overriding current timeout implementation: all
+ stack-internal caclic timers are avaliable in the lwip_cyclic_timers array
+
+ 2016-03-23: Simon Goldschmidt
+ * tcp: call accept-callback with ERR_MEM when allocating a pcb fails on
+ passive open to inform the application about this error
+ ATTENTION: applications have to handle NULL pcb in accept callback!
+
+ 2016-02-22: Ivan Delamer
+ * Initial 6LoWPAN support
+
+ 2016-02-XX to 2016-03-XX: Dirk Ziegelmeier
+ * Cleanup TCPIP thread sync methods in a way that it is possibe to use them
+ in arbitrary code that needs things to be done in TCPIP thread. Used to
+ decouple netconn, netif, ppp and 6LoWPAN from LWIP core.
+
+ 2016-02-XX: Dirk Ziegelmeier
+ * Implement dual-stack support in RAW, UDP and TCP. Add new IP address
+ type IPADDR_ANY_TYPE for this. Netconn/Socket API: Dual-stack is
+ automatically supported when an IPv6 netconn/socket is created.
+
+ 2015-12-26: Martin Hentschel and Dirk Ziegelmeier
+ * Rewrite SNMP agent. SNMPv2c + MIB compiler.
+
+ 2015-11-12: Dirk Ziegelmeier
+ * Decouple SNMP stack from lwIP core and move stack to apps/ directory.
+ Breaking change: Users have to call snmp_init() now!
+
+ 2015-11-12: Dirk Ziegelmeier
+ * Implement possibility to declare private memory pools. This is useful to
+ decouple some apps from the core (SNMP stack) or make contrib app usage
+ simpler (httpserver_raw)
+
+ 2015-10-09: Simon Goldschmidt
+ * started to move "private" header files containing implementation details to
+ "lwip/priv/" include directory to seperate the API from the implementation.
+
+ 2015-10-07: Simon Goldschmidt
+ * added sntp client as first "supported" application layer protocol implementation
+ added 'apps' folder
+
+ 2015-09-30: Dirk Ziegelmeier
+ * snmp_structs.h, mib_structs.c, mib2.c: snmp: fixed ugly inheritance
+ implementation by aggregating the "base class" (struct mib_node) in all
+ derived node classes to get more type-safe code
+
+ 2015-09-23: Simon Goldschmidt
+ * netif.h/.c, nd6.c: task #13729: Convert netif addresses (IPv4 & IPv6) to
+ ip_addr_t (so they can be used without conversion/temporary storage)
+
+ 2015-09-08: Dirk Ziegelmeier
+ * snmp: Separate mib2 counter/table callbacks from snmp agent. This both cleans
+ up the code and should allow integration of a 3rd party agent/mib2. Simple
+ counters are kept in MIB2_STATS, tree/table change function prototypes moved to
+ snmp_mib2.h.
+
+ 2015-09-03: Simon Goldschmidt
+ * opt.h, dns.h/.c: DNS/IPv6: added support for AAAA records
+
+ 2015-09-01: Simon Goldschmidt
+ * task #12178: hardware checksum capabilities can be configured per netif
+ (use NETIF_SET_CHECKSUM_CTRL() in your netif's init function)
+
+ 2015-08-30: Simon Goldschmidt
+ * PBUF_REF with "custom" pbufs is now supported for RX pbufs (see pcapif in
+ contrib for an example, LWIP_SUPPORT_CUSTOM_PBUF is required)
+
+ 2015-08-30: Simon Goldschmidt
+ * support IPv4 source based routing: define LWIP_HOOK_IP4_ROUTE_SRC to point
+ to a routing function
+
+ 2015-08-05: Simon Goldschmidt
+ * many files: allow multicast socket options IP_MULTICAST_TTL, IP_MULTICAST_IF
+ and IP_MULTICAST_LOOP to be used without IGMP
+
+ 2015-04-24: Simon Goldschmidt
+ * dhcp.h/c, autoip.h/.c: added functions dhcp/autoip_supplied_address() to
+ check for the source of address assignment (replacement for NETIF_FLAG_DHCP)
+
+ 2015-04-10: Simon Goldschmidt
+ * many files: task #13480: added LWIP_IPV4 define - IPv4 can be disabled,
+ leaving an IPv6-only stack
+
+ 2015-04-09: Simon Goldschmidt
+ * nearly all files: task #12722 (improve IPv4/v6 address handling): renamed
+ ip_addr_t to ip4_addr_t, renamed ipX_addr_t to ip_addr_t and added IP
+ version; ip_addr_t is used for all generic IP addresses for the API,
+ ip(4/6)_addr_t are only used internally or when initializing netifs or when
+ calling version-related functions
+
+ 2015-03-24: Simon Goldschmidt
+ * opt.h, ip4_addr.h, ip4.c, ip6.c: loopif is not required for loopback traffic
+ any more but passed through any netif (ENABLE_LOOPBACK has to be enabled)
+
+ 2015-03-23: Simon Goldschmidt
+ * opt.h, etharp.c: with ETHARP_TABLE_MATCH_NETIF== 1, duplicate (Auto)-IP
+ addresses on multiple netifs should now be working correctly (if correctly
+ addressed by routing, that is)
+
+ 2015-03-23: Simon Goldschmidt
+ * etharp.c: Stable etharp entries that are about to expire are now refreshed
+ using unicast to prevent unnecessary broadcast. Only if no answer is received
+ after 15 seconds, broadcast is used.
+
+ 2015-03-06: Philip Gladstone
+ * netif.h/.c: patch #8359 (Provide utility function to add an IPv6 address to
+ an interface)
+
+ 2015-03-05: Simon Goldschmidt
+ * netif.c, ip4.c, dhcp.c, autoip.c: fixed bug #37068 (netif up/down handling
+ is unclear): correclty separated administrative status of a netif (up/down)
+ from 'valid address' status
+ ATTENTION: netif_set_up() now always has to be called, even when dhcp/autoip
+ is used!
+
+ 2015-02-26: patch by TabascoEye
+ * netif.c, udp.h/.c: fixed bug #40753 (re-bind UDP pcbs on change of IP address)
+
+ 2015-02-22: chrysn, Simon Goldschmidt
+ * *.*: Changed nearly all functions taking 'ip(X)_addr_t' pointer to take
+ const pointers (changed user callbacks: raw_recv_fn, udp_recv_fn; changed
+ port callbacks: netif_output_fn, netif_igmp_mac_filter_fn)
+
+ 2015-02-19: Ivan Delamer
+ * netif.h, dhcp.c: Removed unused netif flag for DHCP. The preferred way to evaluate
+ if DHCP is active is through netif->dhcp field.
+
+ 2015-02-19: Ivan Delamer
+ * netif.h, slipif.c, ppp.c: Removed unused netif flag for point to point connections
+
+ 2015-02-18: Simon Goldschmidt
+ * api_lib.c: fixed bug #37958 "netconn API doesn't handle correctly
+ connections half-closed by peer"
+
+ 2015-02-18: Simon Goldschmidt
+ * tcp.c: tcp_alloc() prefers killing CLOSING/LAST_ACK over active connections
+ (see bug #39565)
+
+ 2015-02-16: Claudius Zingerli, Sergio Caprile
+ * opt.h, dhcp.h/.c: patch #8361 "Add support for NTP option in DHCP"
+
+ 2015-02-14: Simon Goldschmidt
+ * opt.h, snmp*: added support for write-access community and dedicated
+ community for sending traps
+
+ 2015-02-13: Simon Goldschmidt
+ * opt.h, memp.c: added hook LWIP_HOOK_MEMP_AVAILABLE() to get informed when
+ a memp pool was empty and an item is now available
+
+ 2015-02-13: Simon Goldschmidt
+ * opt.h, pbuf.h/.c, etharp.c: Added the option PBUF_LINK_ENCAPSULATION_HLEN to
+ allocate additional header space for TX on netifs requiring additional headers
+
+ 2015-02-12: chrysn
+ * timers.h/.c: introduce sys_timeouts_sleeptime (returns the time left before
+ the next timeout is due, for NO_SYS==1)
+
+ 2015-02-11: Nick van Ijzendoorn
+ * opt.h, sockets.h/c: patch #7702 "Include ability to increase the socket number
+ with defined offset"
+
+ 2015-02-11: Frederick Baksik
+ * opt.h, def.h, others: patch #8423 "arch/perf.h" should be made an optional item
+
+ 2015-02-11: Simon Goldschmidt
+ * api_msg.c, opt.h: started to implement fullduplex sockets/netconns
+ (note that this is highly unstable yet!)
+
+ 2015-01-17: Simon Goldschmidt
+ * api: allow enabling socket API without (public) netconn API - netconn API is
+ still used by sockets, but keeping it private (static) should allow better
+ compiler optimizations
+
+ 2015-01-16: Simon Goldschmidt
+ * tcp_in.c: fixed bug #20506 "Initial congestion window is very small" again
+ by implementing the calculation formula from RFC3390
+
+ 2014-12-10: Simon Goldschmidt
+ * api: added option LWIP_NETCONN_SEM_PER_THREAD to use a semaphore per thread
+ instead of using one per netconn and per select call
+
+ 2014-12-08: Simon Goldschmidt
+ * ip6.h: fixed bug #43778: IPv6 header version not set on 16-bit platform
+ (macro IP6H_VTCFL_SET())
+
+ 2014-12-08: Simon Goldschmidt
+ * icmp.c, ip4.c, pbuf.c, udp.c, pbuf.h: task #11472 Support PBUF_REF for RX
+ (IPv6 and IPv4/v6 reassembly might not work yet)
+
+ 2014-11-06: Simon Goldschmidt
+ * sockets.c/.h, init.c: lwip_socket_init() is not needed any more
+ -> compatibility define
+
+ 2014-09-16: Simon Goldschmidt
+ * dns.c, opt.h: reduced ram usage by parsing DNS responses in place
+
+ 2014-09-16: Simon Goldschmidt
+ * pbuf.h/.c: added pbuf_take_at() and pbuf_put_at()
+
+ 2014-09-15: Simon Goldschmidt
+ * dns.c: added source port randomization to make the DNS client more robust
+ (see bug #43144)
+
+ 2013-09-02: Simon Goldschmidt
+ * arch.h and many other files: added optional macros PACK_STRUCT_FLD_8() and
+ PACK_STRUCT_FLD_S() to prevent gcc 4 from warning about struct members that
+ do not need packing
+
+ 2013-08-19: Simon Goldschmidt
+ * netif.h: bug #42998: made NETIF_MAX_HWADDR_LEN overridable for some special
+ networks
+
+ 2013-03-17: Simon Goldschmidt (patch by Ghobad Emadi)
+ * opt.h, etharp.c: Added LWIP_HOOK_ETHARP_GET_GW to implement IPv4 routing with
+ multiple gateways
+
+ 2013-04-20: Fatih Asici
+ * opt.h, etharp.h/.c: patch #7993: Added support for transmitting packets
+ with VLAN headers via hook function LWIP_HOOK_VLAN_SET and to check them
+ via hook function LWIP_HOOK_VLAN_CHECK
+
+ 2014-02-20: Simon Goldschmidt (based on patch by Artem Pisarenko)
+ * patch #7885: modification of api modules to support FreeRTOS-MPU
+ (don't pass stack-pointers to other threads)
+
+ 2014-02-05: Simon Goldschmidt (patch by "xtian" and "alex_ab")
+ * patch #6537/#7858: TCP window scaling support
+
+ 2014-01-17: Jiri Engelthaler
+ * icmp, icmp6, opt.h: patch #8027: Completed HW checksuming for IPv4 and
+ IPv6 ICMP's
+
+ 2012-08-22: Sylvain Rochet
+ * New PPP stack for lwIP, developed in ppp-new branch.
+ Based from pppd 2.4.5, released 2009-11-17, with huge changes to match
+ code size and memory requirements for embedded devices, including:
+ - Gluing together the previous low-level PPP code in lwIP to pppd 2.4.5, which
+ is more or less what pppd sys-* files are, so that we get something working
+ using the unix port.
+ - Merged some patchs from lwIP Git repository which add interesting features
+ or fix bugs.
+ - Merged some patchs from Debian pppd package which add interesting features
+ or fix bugs.
+ - Ported PPP timeout handling to the lwIP timers system
+ - Disabled all the PPP code using filesystem access, replaced in necessary cases
+ to configuration variables.
+ - Disabled all the PPP code forking processes.
+ - Removed IPX support, lwIP does not support IPX.
+ - Ported and improved random module from the previous PPP port.
+ - Removed samba TDB (file-driven database) usage, because it needs a filesystem.
+ - MS-CHAP required a DES implementation, we added the latest PolarSSL DES
+ implementation which is under a BSD-ish license.
+ - Also switched to PolarSSL MD4,MD5,SHA1 implementations, which are meant to be
+ used in embedded devices with reduced memory footprint.
+ - Removed PPP configuration file parsing support.
+ - Added macro definition EAP_SUPPORT to make EAP support optional.
+ - Added macro definition CHAP_SUPPORT to make CHAP support optional.
+ - Added macro definition MSCHAP_SUPPORT to make MSCHAP support optional.
+ - Added macro definition PAP_SUPPORT to make PAP support optional.
+ - Cleared all Linux syscall calls.
+ - Disabled demand support using a macro, so that it can be ported later.
+ - Disabled ECP support using a macro, so that it can be ported later.
+ - Disabled CCP support using a macro, so that it can be ported later.
+ - Disabled CBCP support using a macro, so that it can be ported later.
+ - Disabled LQR support using a macro, so that it can be ported later.
+ - Print packet debug feature optional, through PRINTPKT_SUPPORT
+ - Removed POSIX signal usage.
+ - Fully ported PPPoS code from the previous port.
+ - Fully ported PPPoE code from the previous port.
+ - Fully ported VJ compression protocol code from the previous port.
+ - Removed all malloc()/free() use from PPP, replaced by stack usage or PBUF.
+ - Disabled PPP server support using a macro, so that it can be ported later.
+ - Switched all PPP debug to lwIP debug system.
+ - Created PPP Control Block (PPP PCB), removed PPP unit integer everywhere,
+ removed all global variables everywhere, did everything necessary for
+ the PPP stack to support more than one PPP session (pppd only support
+ one session per process).
+ - Removed the statically allocated output buffer, now using PBUF.
+ - Improved structure size of all PPP modules, deep analyze of code to reduce
+ variables size to the bare minimum. Switched all boolean type (char type in
+ most architecture) to compiler generated bitfields.
+ - Added PPP IPv6 support, glued lwIP IPv6 support to PPP.
+ - Now using a persistent netif interface which can then be used in lwIP
+ functions requiring a netif.
+ - Now initializing PPP in lwip_init() function.
+ - Reworked completely the PPP state machine, so that we don't end up in
+ anymore in inconsistent state, especially with PPPoE.
+ - Improved the way we handle PPP reconnection after disconnect, cleaning
+ everything required so that we start the PPP connection again from a
+ clean state.
+ - Added PPP holdoff support, allow the lwIP user to wait a little bit before
+ reconnecting, prevents connection flood, especially when using PPPoL2TP.
+ - Added PPPoL2TP LAC support (a.k.a. UDP tunnels), adding a VPN client
+ feature to lwIP, L2TP being a widely used tunnel protocol.
+ - Switched all used PPP types to lwIP types (u8t, u16t, u32t, ...)
+ - Added PPP API "sequential" thread-safe API, based from NETIFAPI.
+
+ 2011-07-21: Simon Goldschmidt
+ * sockets.c, opt.h: (bug #30185): added LWIP_FIONREAD_LINUXMODE that makes
+ ioctl/FIONREAD return the size of the next pending datagram.
+
+ 2011-05-25: Simon Goldschmidt
+ * again nearly the whole stack, renamed ip.c to ip4.c, ip_addr.c to ip4_addr.c,
+ combined ipv4/ipv6 inet_chksum.c, added ip.h, ip_addr.h: Combined IPv4
+ and IPv6 code where possible, added defines to access IPv4/IPv6 in non-IP
+ code so that the code is more readable.
+
+ 2011-05-17: Patch by Ivan Delamer (only checked in by Simon Goldschmidt)
+ * nearly the whole stack: Finally, we got decent IPv6 support, big thanks to
+ Ivan! (this is work in progress: we're just post release anyway :-)
+
+
+ ++ Bugfixes:
+
+ 2016-08-23: Simon Goldschmidt
+ * etharp: removed ETHARP_TRUST_IP_MAC since it is insecure and we don't need
+ it any more after implementing unicast ARP renewal towards arp entry timeout
+
+ 2016-07-20: Simon Goldschmidt
+ * memp.h/.c: fixed bug #48442 (memp stats don't work for MEMP_MEM_MALLOC)
+
+ 2016-07-21: Simon Goldschmidt (patch by Ambroz Bizjak)
+ * tcp_in.c, tcp_out.c: fixed bug #48543 (TCP sent callback may prematurely
+ report sent data when only part of a segment is acked) and don't include
+ SYN/FIN in snd_buf counter
+
+ 2016-07-19: Simon Goldschmidt
+ * etharp.c: fixed bug #48477 (ARP input packet might update static entry)
+
+ 2016-07-11: Simon Goldschmidt
+ * tcp_in.c: fixed bug #48476 (TCP sent callback called wrongly due to picking
+ up old pcb->acked
+
+ 2016-06-30: Simon Goldschmidt (original patch by Fabian Koch)
+ * tcp_in.c: fixed bug #48170 (Vulnerable to TCP RST spoofing)
+
+ 2016-05-20: Dirk Ziegelmeier
+ * sntp.h/.c: Fix return value of sntp_getserver() call to return a pointer
+
+ 2016-04-05: Simon Goldschmidt (patch by Philip Gladstone)
+ * udp.c: patch #8358: allow more combinations of listening PCB for IPv6
+
+ 2016-04-05: Simon Goldschmidt
+ * netconn/socket API: fixed bug# 43739 (Accept not reporting errors about
+ aborted connections): netconn_accept() returns ERR_ABRT (sockets: ECONNABORTED)
+ for aborted connections, ERR_CLSD (sockets: EINVAL) if the listening netconn
+ is closed, which better seems to follow the standard.
+
+ 2016-03-23: Florent Matignon
+ * dhcp.c: fixed bug #38203: DHCP options are not recorded in all DHCP ack messages
+
+ 2016-03-22: Simon Goldschmidt
+ * tcp: changed accept handling to be done internally: the application does not
+ have to call tcp_accepted() any more. Instead, when delaying accept (e.g. sockets
+ do), call tcp_backlog_delayed()/tcp_backlog_accepted() (fixes bug #46696)
+
+ 2016-03-22: Simon Goldschmidt
+ * dns.c: ignore dns response parsing errors, only abort resolving for correct
+ responses or error responses from correct server (bug #47459)
+
+ 2016-03-17: Simon Goldschmidt
+ * api_msg.c: fixed bug #47448 (netconn/socket leak if RST is received during close)
+
+ 2016-03-17: Joel Cunningham
+ * api_msg.c: don't fail closing a socket/netconn when failing to allocate the
+ FIN segment; blocking the calling thread for a while is better than risking
+ leaking a netconn/socket (see bug #46701)
+
+ 2016-03-16: Joel Cunningham
+ * tcp_out.c: reset rto timer on fast retransmission
+
+ 2016-03-16: Deomid Ryabkov
+ * tcp_out.c: fixed bug #46384 Segment size calculation bug with MSS != TCP_MSS
+
+ 2016-03-05: Simon Goldschmidt
+ * err.h/.c, sockets.c: ERR_IF is not necessarily a fatal error
+
+ 2015-11-19: fix by Kerem Hadimli
+ * sockets.c: fixed bug #46471: lwip_accept() leaks socket descriptors if new
+ netconn was already closed because of peer behavior
+
+ 2015-11-12: fix by Valery Ushakov
+ * tcp_in.c: fixed bug #46365 tcp_accept_null() should call tcp_abort()
+
+ 2015-10-02: Dirk Ziegelmeier/Simon Goldschmidt
+ * snmp: cleaned up snmp structs API (fixed race conditions from bug #46089,
+ reduce ram/rom usage of tables): incompatible change for private MIBs
+
+ 2015-09-30: Simon Goldschmidt
+ * ip4_addr.c: fixed bug #46072: ip4addr_aton() does not check the number range
+ of all address parts
+
+ 2015-08-28: Simon Goldschmidt
+ * tcp.c, tcp_in.c: fixed bug #44023: TCP ssthresh value is unclear: ssthresh
+ is set to the full send window for active open, too, and is updated once
+ after SYN to ensure the correct send window is used
+
+ 2015-08-28: Simon Goldschmidt
+ * tcp: fixed bug #45559: Window scaling casts u32_t to u16_t without checks
+
+ 2015-08-26: Simon Goldschmidt
+ * ip6_frag.h/.c: fixed bug bug #41009: IPv6 reassembly broken on 64-bit platforms:
+ define IPV6_FRAG_COPYHEADER==1 on these platforms to copy the IPv6 header
+ instead of referencing it, which gives more room for struct ip6_reass_helper
+
+ 2015-08-25: Simon Goldschmidt
+ * sockets.c: fixed bug #45827: recvfrom: TCP window is updated with MSG_PEEK
+
+ 2015-08-20: Manoj Kumar
+ * snmp_msg.h, msg_in.c: fixed bug #43790: Sending octet string of Length >255
+ from SNMP agent
+
+ 2015-08-19: Jens Nielsen
+ * icmp.c, ip4.c, tcp_in.c, udp.c, raw.c: fixed bug #45120: Broadcast & multiple
+ interfaces handling
+
+ 2015-08-19: Simon Goldschmidt (patch by "Sandra")
+ * dns.c: fixed bug #45004: dns response without answer might be discarded
+
+ 2015-08-18: Chrysn
+ * timers.c: patch #8704 fix sys_timeouts_sleeptime function
+
+ 2015-07-01: Erik Ekman
+ * puf.c: fixed bug #45454 (pbuf_take_at() skips write and returns OK if offset
+ is at start of pbuf in chain)
+
+ 2015-05-19: Simon Goldschmidt
+ * dhcp.h/.c: fixed bugs #45140 and #45141 (dhcp was not stopped correctly after
+ fixing bug #38204)
+
+ 2015-03-21: Simon Goldschmidt (patch by Homyak)
+ * tcp_in.c: fixed bug #44766 (LWIP_WND_SCALE: tcphdr->wnd was not scaled in
+ two places)
+
+ 2015-03-21: Simon Goldschmidt
+ * tcp_impl.h, tcp.c, tcp_in.c: fixed bug #41318 (Bad memory ref in tcp_input()
+ after tcp_close())
+
+ 2015-03-21: Simon Goldschmidt
+ * tcp_in.c: fixed bug #38468 (tcp_sent() not called on half-open connection for
+ data ACKed with the same ack as FIN)
+
+ 2015-03-21: Simon Goldschmidt (patch by Christoffer Lind)
+ * dhcp.h/.c: fixed bug #38204 (DHCP lease time not handled correctly)
+
+ 2015-03-20: Simon Goldschmidt
+ * dhcp.c: fixed bug #38714 (Missing option and client address in DHCPRELEASE message)
+
+ 2015-03-19: Simon Goldschmidt
+ * api.h, tcpip.h, api_lib.c, api_msg.c: fixed race conditions in assigning
+ netconn->last_err (fixed bugs #38121 and #37676)
+
+ 2015-03-09: Simon Goldschmidt
+ * ip4.c: fixed the IPv4 part of bug #43904 (ip_route() must detect linkup status)
+
+ 2015-03-04: Simon Goldschmidt
+ * nd6.c: fixed bug #43784 (a host should send at least one Router Solicitation)
+
+ 2015-03-04: Valery Ushakov
+ * ip6.c: fixed bug #41094 (Byte-order bug in IPv6 fragmentation header test)
+
+ 2015-03-04: Zach Smith
+ * nd6.c: fixed bug #38153 (nd6_input() byte order issues)
+
+ 2015-02-26: Simon Goldschmidt
+ * netif.c, tcp.h/.c: fixed bug #44378 (TCP connections are not aborted on netif
+ remove)
+
+ 2015-02-25: Simon Goldschmidt
+ * ip4.c, etharp.c: fixed bug #40177 (System hangs when dealing with corrupted
+ packets), implemented task #12357 (Ensure that malicious packets don't
+ assert-fail): improved some pbuf_header calls to not assert-fail.
+
+ 2015-02-25: patch by Joel Cunningham
+ * udp.h/.c, sockets.c: fixed bug #43028 (IP_MULTICAST_TTL affects unicast
+ datagrams)
+
+ 2015-02-25: patch by Greg Renda
+ * ip4_frag.c: fixed bug #38210 (ip reassembly while remove oldest datagram)
+
+ 2015-02-25: Simon Goldschmidt
+ * sockets.c: fixed bug #38165 (socket with mulicast): ensure igmp membership
+ are dropped when socket (not netconn!) is closed.
+
+ 2015-02-25: Simon Goldschmidt
+ * ip4.h/.c, udp.c: fixed bug #38061 (wrong multicast routing in IPv4) by
+ adding an optional default netif for multicast routing
+
+ 2015-02-25: Simon Goldschmidt
+ * netconn API: fixed that netconn_connect still used message passing for
+ LWIP_TCPIP_CORE_LOCKING==1
+
+ 2015-02-22: patch by Jens Nielsen
+ * icmp.c: fixed bug #38803 (Source address in broadcast ping reply)
+
+ 2015-02-22: Simon Goldschmidt
+ * udp.h, sockets.c: added proper accessor functions for pcb->multicast_ip
+ (previously used by get/setsockopt only)
+
+ 2015-02-18: Simon Goldschmidt
+ * sockets.c: Fixed select not reporting received FIN as 'readable' in certain
+ rare cases (bug #43779: select(), close(), and TCP retransmission error)
+
+ 2015-02-17: Simon Goldschmidt
+ * err.h, sockets.c, api_msg.c: fixed bug #38853 "connect() use a wrong errno":
+ return ERR_ALREADY/EALRADY during connect, ERR_ISCONN/EISCONN when already
+ connected
+
+ 2015-02-17: Simon Goldschmidt
+ * tcp_impl.h, tcp_out.c, tcp.c, api_msg.c: fixed bug #37614 "Errors from
+ ipX_output are not processed". Now tcp_output(_segment) checks for the return
+ value of ipX_output and does not try to send more on error. A netif driver
+ can call tcp_txnow() (from tcpip_thread!) to try to send again if TX buffers
+ are available again.
+
+ 2015-02-14: patches by Freddie Chopin
+ * snmp*: made community writable, fixed some const pointers
+
+ 2015-02-13: Simon Goldschmidt
+ * msg_in.c: fixed bug #22070 "MIB_OBJECT_WRITE_ONLY not implemented in SNMP"
+
+ 2015-02-12: Simon Goldschmidt
+ * ip.h, ip4.c, ip6.c: fixed bug #36403 "ip4_input() and ip6_input() always pass
+ inp to higher layers": now the accepting netif is passed up, but the input
+ netif is available through ip_current_input_netif() if required.
+
+ 2015-02-11: patch by hichard
+ * tcpip.c: fixed bug #43094 "The function tcpip_input() forget to handle IPv6"
+
+ 2015-02-10: Simon Goldschmidt
+ * netconn API: fixed that netconn_close/netconn_delete still used message passing
+ for LWIP_TCPIP_CORE_LOCKING==1
+
+ 2015-02-10: Simon Goldschmidt
+ * netconn/socket api: fixed bug #44225 "closing TCP socket should time out
+ eventually", implemented task #6930 "Implement SO_LINGER": closing TCP sockets
+ times out after 20 seconds or after the configured SND_TIMEOUT or depending
+ on the linger settings.
+
+ 2015-01-27: Simon Goldschmidt
+ * api_msg.c: fixed that SHUT_RD followed by SHUT_WR was different to SHUT_RDWR,
+ fixed return value of lwip_netconn_do_close on unconnected netconns
+
+ 2015-01-17: Simon Goldschmidt
+ * sockets.c: fixed bug #43361 select() crashes with stale FDs
+
+ 2015-01-17: Simon Goldschmidt
+ * sockets.c/.h, memp_std.h: fixed bug #40788 "lwip_setsockopt_internal() crashes"
+ by rewriting set/getsockopt functions to combine checks with the actual code
+ and add more NULL checks; this also fixes that CORE_LOCKING used message
+ passing for set/getsockopt.
+
+ 2014-12-19: Simon Goldschmidt
+ * opt.h, dhcp.h/.c: prevent dhcp from starting when netif link is down (only
+ when LWIP_DHCP_CHECK_LINK_UP==1, which is disabled by default for
+ compatibility reasons)
+
+ 2014-12-17: Simon Goldschmidt
+ * tcp_out.c: fixed bug #43840 Checksum error for TCP_CHECKSUM_ON_COPY==1 for
+ no-copy data with odd length
+
+ 2014-12-10: Simon Goldschmidt
+ * sockets.c, tcp.c, others: fixed bug #43797 set/getsockopt: SO_SNDTIMEO/SO_RCVTIMEO
+ take int as option but should take timeval (LWIP_SO_SNDRCVTIMEO_STANDARD==0 can
+ be used to revert to the old 'winsock' style behaviour)
+ Fixed implementation of SO_ACCEPTCONN to just look at the pcb state
+
+ 2014-12-09: Simon Goldschmidt
+ * ip4.c: fixed bug #43596 IGMP queries from 0.0.0.0 are discarded
+
+ 2014-10-21: Simon Goldschmidt (patch by Joel Cunningham and Albert Huitsing)
+ * sockts.c: fixed bugs #41495 Possible threading issue in select() and #43278
+ event_callback() handle context switch when calling sys_sem_signal()
+
+ 2014-10-21: Simon Goldschmidt
+ * api_msg.c: fixed bug #38219 Assert on TCP netconn_write with sndtimeout set
+
+ 2014-09-16: Kevin Cernekee
+ * dns.c: patch #8480 Fix handling of dns_seqno wraparound
+
+ 2014-09-16: Simon Goldschmidt
+ * tcp_out.c: fixed bug #43192 tcp_enqueue_flags() should not check TCP_SND_QUEUELEN
+ when sending FIN
+
+ 2014-09-03: Simon Goldschmidt
+ * msg_in.c: fixed bug #39355 SNMP Memory Leak in case of error
+
+ 2014-09-02: Simon Goldschmidt
+ * err.h/.c, sockets.c, api_msg.c: fixed bug #43110 call getpeername() before
+ listen() will cause a error
+
+ 2014-09-02: Simon Goldschmidt
+ * sockets.c: fixed bug #42117 lwip_fcntl does not set errno
+
+ 2014-09-02: Simon Goldschmidt
+ * tcp.c: fixed bug #42299 tcp_abort() leaves freed pcb on tcp_bound_pcbs list
+
+ 2014-08-20: Simon Goldschmidt
+ * dns.c: fixed bug #42987 lwIP is vulnerable to DNS cache poisoning due to
+ non-randomized TXIDs
+
+ 2014-06-03: Simon Goldschmidt
+ * tcp_impl.h, tcp_in.c: fixed bug #37969 SYN packet dropped as short packet in
+ tcp_input function
+
+ 2014-05-20: Simon Goldschmidt
+ * tcp_out.c: fixed bug #37184 tcp_write problem for pcbs in the SYN_SENT state
+
+ 2014-05-19: Simon Goldschmidt
+ * *.h: Fixed bug #35874 reserved identifier violation (removed leading underscores
+ from header include guards)
+
+ 2014-04-08: Simon Goldschmidt
+ * tcp.c: Fixed bug #36167 tcp server crash when client closes (maximum window)
+
+ 2014-04-06: Simon Goldschmidt
+ * tcp_in.c: Fixed bug #36210 lwIP does not elicit an empty ACK when received
+ unacceptable ACK
+
+ 2014-04-06: Simon Goldschmidt
+ * dhcp.c, ip4.c/.h, ip6.c/.h, udp.c/.h, ip.h: Fixed bug #41787 DHCP Discovery
+ is invalid when an IP is set to thet netif.
+
+ 2014-03-14: Simon Goldschmidt
+ * tcp_out.c: Fixed bug #36153 TCP Cheksum error if LWIP_CHECKSUM_ON_COPY=1
+
+ 2014-03-11: Simon Goldschmidt (patch by Mason)
+ * opt.h, sockets.c: fixed bug #35928 BSD sockets functions must set errno for
+ POSIX-compliance
+
+ 2014-02-27: Simon Goldschmidt
+ * dhcp.c: fixed bug #40303 DHCP xid renewed when sending a DHCPREQUEST
+
+ 2014-02-27: Simon Goldschmidt
+ * raw.c: fixed bug #41680 raw socket can not receive IPv6 packet when
+ IP_SOF_BROADCAST_RECV==1
+
+ 2014-02-27: Simon Goldschmidt
+ * api_msg.c, sockets.c: fixed bug #38404 getpeeraddr returns success on
+ unconnected/listening TCP sockets
+
+ 2014-02-27: Simon Goldschmidt
+ * sockets.c: fixed bug #41729 Some socket functions return Exyz instead of -1
+
+ 2014-02-25: Simon Goldschmidt
+ * ip4.c: fixed bug #39514 ip_route() may return an IPv6-only interface
+
+ 2014-02-25: Simon Goldschmidt, patch by Fatih Asici
+ * pbuf.c: fixed bug #39356 Wrong increment in pbuf_memfind()
+
+ 2014-02-25: Simon Goldschmidt
+ * netif.c/.h, udp.c: fixed bug #39225 udp.c uses netif_matches_ip6_addr() incorrectly;
+ renamed function netif_matches_ip6_addr() to netif_get_ip6_addr_match()
+
+ 2014-02-25: Simon Goldschmidt
+ * igmp.c: fixed bug #39145 IGMP membership report for 224.0.0.1
+
+ 2014-02-22: Simon Goldschmidt (patch by Amir Shalem)
+ * etharp.c, opt.h: fixed bug #34681 Limit ARP queue length by ARP_QUEUE_LEN (=3)
+
+ 2014-02-22: Simon Goldschmidt (patch by Amir Shalem)
+ * etharp.h/.c: fixed bug #34682 Limit ARP request flood for unresolved entry
+
+ 2014-02-20: Simon Goldschmidt
+ * tcp_out.c: fixed bug #39683 Assertion "seg->tcphdr not aligned" failed with
+ MEM_ALIGNMENT = 8
+
+ 2014-02-20: Simon Goldschmidt
+ * sockets.c: fixed bug #39882 No function shall set errno to 0
+
+ 2014-02-20: Simon Goldschmidt
+ * mib_structs.c: fixed bug #40050 SNMP problem with MIB arrays > 255
+
+ 2014-02-20: Simon Goldschmidt
+ * api.h, sockets.c: fixed bug #41499 netconn::recv_avail can overflow
+
+ 2014-01-08: Stathis Voukelatos
+ * memp_std.h: patch #7928 Fixed size calculation in MALLOC memory pool
+ creation macro
+
+ 2014-01-18: Brian Fahs
+ * tcp_out.c: patch #8237: tcp_rexmit_rto fails to update pcb->unsent_oversize
+ when necessary
+
+ 2014-01-17: Grant Erickson, Jay Logue, Simon Goldschmidt
+ * ipv6.c, netif.c: patch #7913 Enable Support for IPv6 Loopback
+
+ 2014-01-16: Stathis Voukelatos
+ * netif.c: patch #7902 Fixed netif_poll() operation when LWIP_LOOPBACK_MAX_PBUFS > 0
+
+ 2014-01-14: "Freddie Chopin"
+ * snmp.h, mib2.c: fixed constness and spelling of sysdescr
+
+ 2014-01-14: Simon Goldschmidt (patch by Thomas Faber)
+ * tcpip.c: patch #8241: Fix implicit declaration of ip_input with
+ LWIP_TCPIP_CORE_LOCKING_INPUT disabled
+
+ 2014-01-14: chrysn
+ * timers.c: patch #8244 make timeouts usable reliably from outside of the
+ timeout routine
+
+ 2014-01-10: Simon Goldschmidt
+ * ip_frag.c, ip6_frag.c: fixed bug #41041 Potential use-after-free in IPv6 reassembly
+
+ 2014-01-10: Simon Goldschmidt
+ * memp.c: fixed bug #41188 Alignment error in memp_init() when MEMP_SEPARATE_POOLS==1
+
+ 2014-01-10: Simon Goldschmidt
+ * tcp.c: fixed bug #39898 tcp_fasttmr() possible lock due to infinte queue process loop
+
+ 2013-06-29: Simon Goldschmidt
+ * inet.h, sockets.h: partially fixed bug #37585: IPv6 compatibility (in socket structs)
+
+ 2013-06-29: Simon Goldschmidt
+ * inet6.h: bug #37585/task #12600: fixed struct in6_addr.s6_addr to conform to spec
+
+ 2013-04-24: patch by Liam <morepork>
+ * api_msg.c: patch #8008 Fix a potential null pointer dereference in assert
+
+ 2013-04-24: Simon Goldschmidt
+ * igmp.c: fixed possible division by zero
+
+ 2013-04-24: Simon Goldschmidt
+ * ip6.h, some ipv6 C files: fixed bug #38526 Coverity: Recursive Header Inclusion in ip6.h
+
+ 2013-04-24: Simon Goldschmidt (patch by Emil Ljungdahl):
+ * netif.c: fixed bug #38586 netif_loop_output() "deadlocks"
+
+ 2013-01-15: Simon Goldschmidt
+ * ip4.c: fixed bug #37665 ip_canforward operates on address in wrong byte order
+
+ 2013-01-15: Simon Goldschmidt
+ * pbuf.h: fixed bug #38097 pbuf_free_ooseq() warning
+
+ 2013-01-14: Simon Goldschmidt
+ * dns.c: fixed bug #37705 Possible memory corruption in DNS query
+
+ 2013-01-11: Simon Goldschmidt
+ * raw.c: fixed bug #38066 Raw pcbs can alter packet without eating it
+
+ 2012-08-22: Simon Goldschmidt
+ * memp.c: fixed bug #37166: memp_sanity check loops itself
+
+ 2012-08-13: Simon Goldschmidt
+ * dhcp.c: fixed bug #36645: Calling dhcp_release before dhcp_start
+ dereferences NULL
+
+ 2012-08-13: Simon Goldschmidt
+ * msg_out.c: fixed bug #36840 snmp_send_trap() NULL de-reference if traps
+ configured but no interfaces available
+
+ 2012-08-13: Simon Goldschmidt
+ * dns.c: fixed bug #36899 DNS TTL 0 is cached for a long time
+
+ 2012-05-11: Simon Goldschmidt (patch by Marty)
+ * memp.c: fixed bug #36412: memp.c does not compile when
+ MEMP_OVERFLOW_CHECK > zero and MEMP_SEPARATE_POOLS == 1
+
+ 2012-05-03: Simon Goldschmidt (patch by Sylvain Rochet)
+ * ppp.c: fixed bug #36283 (PPP struct used on header size computation and
+ not packed)
+
+ 2012-05-03: Simon Goldschmidt (patch by David Empson)
+ * ppp.c: fixed bug #36388 (PPP: checksum-only in last pbuf leads to pbuf with
+ zero length)
+
+ 2012-03-25: Simon Goldschmidt
+ * api_msg.c: Fixed bug #35817: do_connect() invalidly signals op_completed
+ for UDP/RAW with LWIP_TCPIP_CORE_LOCKING==1
+
+ 2012-03-25: Simon Goldschmidt
+ * api_msg.h, api_lib.c, api_msg.c, netifapi.c: fixed bug #35931: Name space
+ pollution in api_msg.c and netifapi.c
+
+ 2011-08-24: Simon Goldschmidt
+ * inet6.h: fixed bug #34124 struct in6_addr does not conform to the standard
+
+
+
+(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:
+
+ 2011-03-27: Simon Goldschmidt
+ * tcp_impl.h, tcp_in.c, tcp_out.c: Removed 'dataptr' from 'struct tcp_seg' and
+ calculate it in tcp_zero_window_probe (the only place where it was used).
+
+ 2010-11-21: Simon Goldschmidt
+ * dhcp.c/.h: Added a function to deallocate the struct dhcp from a netif
+ (fixes bug #31525).
+
+ 2010-07-12: Simon Goldschmidt (patch by Stephane Lesage)
+ * ip.c, udp.c/.h, pbuf.h, sockets.c: task #10495: Added support for
+ IP_MULTICAST_LOOP at socket- and raw-API level.
+
+ 2010-06-16: Simon Goldschmidt
+ * ip.c: Added an optional define (LWIP_IP_ACCEPT_UDP_PORT) that can allow
+ link-layer-addressed UDP traffic to be received while a netif is down (just
+ like DHCP during configuration)
+
+ 2010-05-22: Simon Goldschmidt
+ * many many files: bug #27352: removed packing from ip_addr_t, the packed
+ version is now only used in protocol headers. Added global storage for
+ current src/dest IP address while in input functions.
+
+ 2010-05-16: Simon Goldschmidt
+ * def.h: task #10391: Add preprocessor-macros for compile-time htonl
+ calculation (and use them throughout the stack where applicable)
+
+ 2010-05-16: Simon Goldschmidt
+ * opt.h, memp_std.h, memp.c, ppp_oe.h/.c: PPPoE now uses its own MEMP pool
+ instead of the heap (moved struct pppoe_softc from ppp_oe.c to ppp_oe.h)
+
+ 2010-05-16: Simon Goldschmidt
+ * opt.h, memp_std.h, dns.h/.c: DNS_LOCAL_HOSTLIST_IS_DYNAMIC uses its own
+ MEMP pool instead of the heap
+
+ 2010-05-13: Simon Goldschmidt
+ * tcp.c, udp.c: task #6995: Implement SO_REUSEADDR (correctly), added
+ new option SO_REUSE_RXTOALL to pass received UDP broadcast/multicast
+ packets to more than one pcb.
+
+ 2010-05-02: Simon Goldschmidt
+ * netbuf.h/.c, sockets.c, api_msg.c: use checksum-on-copy for sending
+ UDP data for LWIP_NETIF_TX_SINGLE_PBUF==1
+
+ 2010-04-30: Simon Goldschmidt
+ * udp.h/.c, pbuf.h/.c: task #6849: added udp_send(_to/_if) functions that
+ take a precalculated checksum, added pbuf_fill_chksum() to copy data
+ into a pbuf and at the same time calculating the checksum for that data
+
+ 2010-04-29: Simon Goldschmidt
+ * ip_addr.h, etharp.h/.c, autoip.c: Create overridable macros for copying
+ 2-byte-aligned IP addresses and MAC addresses
+
+ 2010-04-28: Patch by Bill Auerbach
+ * ip.c: Inline generating IP checksum to save a function call
+
+ 2010-04-14: Simon Goldschmidt
+ * tcpip.h/.c, timers.c: Added an overridable define to get informed when the
+ tcpip_thread processes messages or timeouts to implement a watchdog.
+
+ 2010-03-28: Simon Goldschmidt
+ * ip_frag.c: create a new (contiguous) PBUF_RAM for every outgoing
+ fragment if LWIP_NETIF_TX_SINGLE_PBUF==1
+
+ 2010-03-27: Simon Goldschmidt
+ * etharp.c: Speedup TX by moving code from find_entry to etharp_output/
+ etharp_query to prevent unnecessary function calls (inspired by
+ patch #7135).
+
+ 2010-03-20: Simon Goldschmidt
+ * opt.h, tcpip.c/.h: Added an option to disable tcpip_(un)timeout code
+ since the linker cannot do this automatically to save space.
+
+ 2010-03-20: Simon Goldschmidt
+ * opt.h, etharp.c/.h: Added support for static ARP table entries
+
+ 2010-03-14: Simon Goldschmidt
+ * tcp_impl.h, tcp_out.c, inet_chksum.h/.c: task #6849: Calculate checksum
+ when creating TCP segments, not when (re-)transmitting them.
+
+ 2010-03-07: Simon Goldschmidt
+ * sockets.c: bug #28775 (select/event_callback: only check select_cb_list
+ on change) plus use SYS_LIGHTWEIGHT_PROT to protect the select code.
+ This should speed up receiving data on sockets as the select code in
+ event_callback is only executed when select is waiting.
+
+ 2010-03-06: Simon Goldschmidt
+ * tcp_out.c: task #7013 (Create option to have all packets delivered to
+ netif->output in one piece): Always copy to try to create single pbufs
+ in tcp_write.
+
+ 2010-03-06: Simon Goldschmidt
+ * api.h, api_lib.c, sockets.c: task #10167 (sockets: speed up TCP recv
+ by not allocating a netbuf): added function netconn_recv_tcp_pbuf()
+ for tcp netconns to receive pbufs, not netbufs; use that function
+ for tcp sockets.
+
+ 2010-03-05: Jakob Ole Stoklundsen / Simon Goldschmidt
+ * opt.h, tcp.h, tcp_impl.h, tcp.c, tcp_in.c, tcp_out.c: task #7040:
+ Work on tcp_enqueue: Don't waste memory when chaining segments,
+ added option TCP_OVERSIZE to prevent creating many small pbufs when
+ calling tcp_write with many small blocks of data. Instead, pbufs are
+ allocated larger than needed and the space is used for later calls to
+ tcp_write.
+
+ 2010-02-21: Simon Goldschmidt
+ * stats.c/.h: Added const char* name to mem- and memp-stats for easier
+ debugging.
+
+ 2010-02-21: Simon Goldschmidt
+ * tcp.h (and usages), added tcp_impl.h: Splitted API and internal
+ implementation of tcp to make API usage cleare to application programmers
+
+ 2010-02-14: Simon Goldschmidt/Stephane Lesage
+ * ip_addr.h: Improved some defines working on ip addresses, added faster
+ macro to copy addresses that cannot be NULL
+
+ 2010-02-13: Simon Goldschmidt
+ * api.h, api_lib.c, api_msg.c, sockets.c: task #7865 (implement non-
+ blocking send operation)
+
+ 2010-02-12: Simon Goldschmidt
+ * sockets.c/.h: Added a minimal version of posix fctl() to have a
+ standardised way to set O_NONBLOCK for nonblocking sockets.
+
+ 2010-02-12: Simon Goldschmidt
+ * dhcp.c/.h, autoip.c/.h: task #10139 (Prefer statically allocated
+ memory): added autoip_set_struct() and dhcp_set_struct() to let autoip
+ and dhcp work with user-allocated structs instead of callin mem_malloc
+
+ 2010-02-12: Simon Goldschmidt/Jeff Barber
+ * tcp.c/h: patch #6865 (SO_REUSEADDR for TCP): if pcb.so_options has
+ SOF_REUSEADDR set, allow binding to endpoint in TIME_WAIT
+
+ 2010-02-12: Simon Goldschmidt
+ * sys layer: task #10139 (Prefer statically allocated memory): converted
+ mbox and semaphore functions to take pointers to sys_mbox_t/sys_sem_t;
+ converted sys_mbox_new/sys_sem_new to take pointers and return err_t;
+ task #7212: Add Mutex concept in sys_arch (define LWIP_COMPAT_MUTEX
+ to let sys.h use binary semaphores instead of mutexes - as before)
+
+ 2010-02-09: Simon Goldschmidt (Simon Kallweit)
+ * timers.c/.h: Added function sys_restart_timeouts() from patch #7085
+ (Restart system timeout handling)
+
+ 2010-02-09: Simon Goldschmidt
+ * netif.c/.h, removed loopif.c/.h: task #10153 (Integrate loopif into
+ netif.c) - loopif does not have to be created by the port any more,
+ just define LWIP_HAVE_LOOPIF to 1.
+
+ 2010-02-08: Simon Goldschmidt
+ * inet.h, ip_addr.c/.h: Added reentrant versions of inet_ntoa/ipaddr_ntoa
+ inet_ntoa_r/ipaddr_ntoa_r
+
+ 2010-02-08: Simon Goldschmidt
+ * netif.h: Added netif_s/get_igmp_mac_filter() macros
+
+ 2010-02-05: Simon Goldschmidt
+ * netif.h: Added function-like macros to get/set the hostname on a netif
+
+ 2010-02-04: Simon Goldschmidt
+ * nearly every file: Replaced struct ip_addr by typedef ip_addr_t to
+ make changing the actual implementation behind the typedef easier.
+
+ 2010-02-01: Simon Goldschmidt
+ * opt.h, memp_std.h, dns.h, netdb.c, memp.c: Let netdb use a memp pool
+ for allocating memory when getaddrinfo() is called.
+
+ 2010-01-31: Simon Goldschmidt
+ * dhcp.h, dhcp.c: Reworked the code that parses DHCP options: parse
+ them once instead of parsing for every option. This also removes
+ the need for mem_malloc from dhcp_recv and makes it possible to
+ correctly retrieve the BOOTP file.
+
+ 2010-01-30: simon Goldschmidt
+ * sockets.c: Use SYS_LIGHTWEIGHT_PROT instead of a semaphore to protect
+ the sockets array.
+
+ 2010-01-29: Simon Goldschmidt (patch by Laura Garrett)
+ * api.h, api_msg.c, sockets.c: Added except set support in select
+ (patch #6860)
+
+ 2010-01-29: Simon Goldschmidt (patch by Laura Garrett)
+ * api.h, sockets.h, err.h, api_lib.c, api_msg.c, sockets.c, err.c:
+ Add non-blocking support for connect (partly from patch #6860),
+ plus many cleanups in socket & netconn API.
+
+ 2010-01-27: Simon Goldschmidt
+ * opt.h, tcp.h, init.c, api_msg.c: Added TCP_SNDQUEUELOWAT corresponding
+ to TCP_SNDLOWAT and added tcp_sndqueuelen() - this fixes bug #28605
+
+ 2010-01-26: Simon Goldschmidt
+ * snmp: Use memp pools for snmp instead of the heap; added 4 new pools.
+
+ 2010-01-14: Simon Goldschmidt
+ * ppp.c/.h: Fixed bug #27856: PPP: Set netif link- and status-callback
+ by adding ppp_set_netif_statuscallback()/ppp_set_netif_linkcallback()
+
+ 2010-01-13: Simon Goldschmidt
+ * mem.c: The heap now may be moved to user-defined memory by defining
+ LWIP_RAM_HEAP_POINTER as a void pointer to that memory's address
+ (patch #6966 and bug #26133)
+
+ 2010-01-10: Simon Goldschmidt (Bill Auerbach)
+ * opt.h, memp.c: patch #6822 (Add option to place memory pools in
+ separate arrays)
+
+ 2010-01-10: Simon Goldschmidt
+ * init.c, igmp.c: patch #6463 (IGMP - Adding Random Delay): added define
+ LWIP_RAND() for lwip-wide randomization (to be defined in cc.h)
+
+ 2009-12-31: Simon Goldschmidt
+ * tcpip.c, init.c, memp.c, sys.c, memp_std.h, sys.h, tcpip.h
+ added timers.c/.h: Separated timer implementation from semaphore/mbox
+ implementation, moved timer implementation to timers.c/.h, timers are
+ now only called from tcpip_thread or by explicitly checking them.
+ (TASK#7235)
+
+ 2009-12-27: Simon Goldschmidt
+ * opt.h, etharp.h/.c, init.c, tcpip.c: Added an additional option
+ LWIP_ETHERNET to support ethernet without ARP (necessary for pure PPPoE)
+
+
+ ++ Bugfixes:
+
+ 2011-04-20: Simon Goldschmidt
+ * sys_arch.txt: sys_arch_timeouts() is not needed any more.
+
+ 2011-04-13: Simon Goldschmidt
+ * tcp.c, udp.c: Fixed bug #33048 (Bad range for IP source port numbers) by
+ using ports in the IANA private/dynamic range (49152 through 65535).
+
+ 2011-03-29: Simon Goldschmidt, patch by Emil Lhungdahl:
+ * etharp.h/.c: Fixed broken VLAN support.
+
+ 2011-03-27: Simon Goldschmidt
+ * tcp.c: Fixed bug #32926 (TCP_RMV(&tcp_bound_pcbs) is called on unbound tcp
+ pcbs) by checking if the pcb was bound (local_port != 0).
+
+ 2011-03-27: Simon Goldschmidt
+ * ppp.c: Fixed bug #32280 (ppp: a pbuf is freed twice)
+
+ 2011-03-27: Simon Goldschmidt
+ * sockets.c: Fixed bug #32906: lwip_connect+lwip_send did not work for udp and
+ raw pcbs with LWIP_TCPIP_CORE_LOCKING==1.
+
+ 2011-03-27: Simon Goldschmidt
+ * tcp_out.c: Fixed bug #32820 (Outgoing TCP connections created before route
+ is present never times out) by starting retransmission timer before checking
+ route.
+
+ 2011-03-22: Simon Goldschmidt
+ * ppp.c: Fixed bug #32648 (PPP code crashes when terminating a link) by only
+ calling sio_read_abort() if the file descriptor is valid.
+
+ 2011-03-14: Simon Goldschmidt
+ * err.h/.c, sockets.c, api_msg.c: fixed bug #31748 (Calling non-blocking connect
+ more than once can render a socket useless) since it mainly involves changing
+ "FATAL" classification of error codes: ERR_USE and ERR_ISCONN just aren't fatal.
+
+ 2011-03-13: Simon Goldschmidt
+ * sockets.c: fixed bug #32769 (ESHUTDOWN is linux-specific) by fixing
+ err_to_errno_table (ERR_CLSD: ENOTCONN instead of ESHUTDOWN), ERR_ISCONN:
+ use EALRADY instead of -1
+
+ 2011-03-13: Simon Goldschmidt
+ * api_lib.c: netconn_accept: return ERR_ABRT instead of ERR_CLSD if the
+ connection has been aborted by err_tcp (since this is not a normal closing
+ procedure).
+
+ 2011-03-13: Simon Goldschmidt
+ * tcp.c: tcp_bind: return ERR_VAL instead of ERR_ISCONN when trying to bind
+ with pcb->state != CLOSED
+
+ 2011-02-17: Simon Goldschmidt
+ * rawapi.txt: Fixed bug #32561 tcp_poll argument definition out-of-order in
+ documentation
+
+ 2011-02-17: Simon Goldschmidt
+ * many files: Added missing U/UL modifiers to fix 16-bit-arch portability.
+
+ 2011-01-24: Simon Goldschmidt
+ * sockets.c: Fixed bug #31741: lwip_select seems to have threading problems
+
+ 2010-12-02: Simon Goldschmidt
+ * err.h: Fixed ERR_IS_FATAL so that ERR_WOULDBLOCK is not fatal.
+
+ 2010-11-23: Simon Goldschmidt
+ * api.h, api_lib.c, api_msg.c, sockets.c: netconn.recv_avail is only used for
+ LWIP_SO_RCVBUF and ioctl/FIONREAD.
+
+ 2010-11-23: Simon Goldschmidt
+ * etharp.c: Fixed bug #31720: ARP-queueing: RFC 1122 recommends to queue at
+ least 1 packet -> ARP_QUEUEING==0 now queues the most recent packet.
+
+ 2010-11-23: Simon Goldschmidt
+ * tcp_in.c: Fixed bug #30577: tcp_input: don't discard ACK-only packets after
+ refusing 'refused_data' again.
+
+ 2010-11-22: Simon Goldschmidt
+ * sockets.c: Fixed bug #31590: getsockopt(... SO_ERROR ...) gives EINPROGRESS
+ after a successful nonblocking connection.
+
+ 2010-11-22: Simon Goldschmidt
+ * etharp.c: Fixed bug #31722: IP packets sent with an AutoIP source addr
+ must be sent link-local
+
+ 2010-11-22: Simon Goldschmidt
+ * timers.c: patch #7329: tcp_timer_needed prototype was ifdef'ed out for
+ LWIP_TIMERS==0
+
+ 2010-11-20: Simon Goldschmidt
+ * sockets.c: Fixed bug #31170: lwip_setsockopt() does not set socket number
+
+ 2010-11-20: Simon Goldschmidt
+ * sockets.h: Fixed bug #31304: Changed SHUT_RD, SHUT_WR and SHUT_RDWR to
+ resemble other stacks.
+
+ 2010-11-20: Simon Goldschmidt
+ * dns.c: Fixed bug #31535: TCP_SND_QUEUELEN must be at least 2 or else
+ no-copy TCP writes will never succeed.
+
+ 2010-11-20: Simon Goldschmidt
+ * dns.c: Fixed bug #31701: Error return value from dns_gethostbyname() does
+ not match documentation: return ERR_ARG instead of ERR_VAL if not
+ initialized or wrong argument.
+
+ 2010-10-20: Simon Goldschmidt
+ * sockets.h: Fixed bug #31385: sizeof(struct sockaddr) is 30 but should be 16
+
+ 2010-10-05: Simon Goldschmidt
+ * dhcp.c: Once again fixed #30038: DHCP/AutoIP cooperation failed when
+ replugging the network cable after an AutoIP address was assigned.
+
+ 2010-08-10: Simon Goldschmidt
+ * tcp.c: Fixed bug #30728: tcp_new_port() did not check listen pcbs
+
+ 2010-08-03: Simon Goldschmidt
+ * udp.c, raw.c: Don't chain empty pbufs when sending them (fixes bug #30625)
+
+ 2010-08-01: Simon Goldschmidt (patch by Greg Renda)
+ * ppp.c: Applied patch #7264 (PPP protocols are rejected incorrectly on big
+ endian architectures)
+
+ 2010-07-28: Simon Goldschmidt
+ * api_lib.c, api_msg.c, sockets.c, mib2.c: Fixed compilation with TCP or UDP
+ disabled.
+
+ 2010-07-27: Simon Goldschmidt
+ * tcp.c: Fixed bug #30565 (tcp_connect() check bound list): that check did no
+ harm but never did anything
+
+ 2010-07-21: Simon Goldschmidt
+ * ip.c: Fixed invalid fix for bug #30402 (CHECKSUM_GEN_IP_INLINE does not
+ add IP options)
+
+ 2010-07-16: Kieran Mansley
+ * msg_in.c: Fixed SNMP ASN constant defines to not use ! operator
+
+ 2010-07-10: Simon Goldschmidt
+ * ip.c: Fixed bug #30402: CHECKSUM_GEN_IP_INLINE does not add IP options
+
+ 2010-06-30: Simon Goldschmidt
+ * api_msg.c: fixed bug #30300 (shutdown parameter was not initialized in
+ netconn_delete)
+
+ 2010-06-28: Kieran Mansley
+ * timers.c remove unportable printing of C function pointers
+
+ 2010-06-24: Simon Goldschmidt
+ * init.c, timers.c/.h, opt.h, memp_std.h: From patch #7221: added flag
+ NO_SYS_NO_TIMERS to drop timer support for NO_SYS==1 for easier upgrading
+
+ 2010-06-24: Simon Goldschmidt
+ * api(_lib).c/.h, api_msg.c/.h, sockets.c/.h: Fixed bug #10088: Correctly
+ implemented shutdown at socket level.
+
+ 2010-06-21: Simon Goldschmidt
+ * pbuf.c/.h, ip_frag.c/.h, opt.h, memp_std.h: Fixed bug #29361 (ip_frag has
+ problems with zero-copy DMA MACs) by adding custom pbufs and implementing
+ custom pbufs that reference other (original) pbufs. Additionally set
+ IP_FRAG_USES_STATIC_BUF=0 as default to be on the safe side.
+
+ 2010-06-15: Simon Goldschmidt
+ * dhcp.c: Fixed bug #29970: DHCP endian issue parsing option responses
+
+ 2010-06-14: Simon Goldschmidt
+ * autoip.c: Fixed bug #30039: AutoIP does not reuse previous addresses
+
+ 2010-06-12: Simon Goldschmidt
+ * dhcp.c: Fixed bug #30038: dhcp_network_changed doesn't reset AUTOIP coop
+ state
+
+ 2010-05-17: Simon Goldschmidt
+ * netdb.c: Correctly NULL-terminate h_addr_list
+
+ 2010-05-16: Simon Goldschmidt
+ * def.h/.c: changed the semantics of LWIP_PREFIX_BYTEORDER_FUNCS to prevent
+ "symbol already defined" i.e. when linking to winsock
+
+ 2010-05-05: Simon Goldschmidt
+ * def.h, timers.c: Fixed bug #29769 (sys_check_timeouts: sys_now() may
+ overflow)
+
+ 2010-04-21: Simon Goldschmidt
+ * api_msg.c: Fixed bug #29617 (sometime cause stall on delete listening
+ connection)
+
+ 2010-03-28: Luca Ceresoli
+ * ip_addr.c/.h: patch #7143: Add a few missing const qualifiers
+
+ 2010-03-27: Luca Ceresoli
+ * mib2.c: patch #7130: remove meaningless const qualifiers
+
+ 2010-03-26: Simon Goldschmidt
+ * tcp_out.c: Make LWIP_NETIF_TX_SINGLE_PBUF work for TCP, too
+
+ 2010-03-26: Simon Goldschmidt
+ * various files: Fixed compiling with different options disabled (TCP/UDP),
+ triggered by bug #29345; don't allocate acceptmbox if LWIP_TCP is disabled
+
+ 2010-03-25: Simon Goldschmidt
+ * sockets.c: Fixed bug #29332: lwip_select() processes readset incorrectly
+
+ 2010-03-25: Simon Goldschmidt
+ * tcp_in.c, test_tcp_oos.c: Fixed bug #29080: Correctly handle remote side
+ overrunning our rcv_wnd in ooseq case.
+
+ 2010-03-22: Simon Goldschmidt
+ * tcp.c: tcp_listen() did not copy the pcb's prio.
+
+ 2010-03-19: Simon Goldschmidt
+ * snmp_msg.c: Fixed bug #29256: SNMP Trap address was not correctly set
+
+ 2010-03-14: Simon Goldschmidt
+ * opt.h, etharp.h: Fixed bug #29148 (Incorrect PBUF_POOL_BUFSIZE for ports
+ where ETH_PAD_SIZE > 0) by moving definition of ETH_PAD_SIZE to opt.h
+ and basing PBUF_LINK_HLEN on it.
+
+ 2010-03-08: Simon Goldschmidt
+ * netif.c, ipv4/ip.c: task #10241 (AutoIP: don't break existing connections
+ when assiging routable address): when checking incoming packets and
+ aborting existing connection on address change, filter out link-local
+ addresses.
+
+ 2010-03-06: Simon Goldschmidt
+ * sockets.c: Fixed LWIP_NETIF_TX_SINGLE_PBUF for LWIP_TCPIP_CORE_LOCKING
+
+ 2010-03-06: Simon Goldschmidt
+ * ipv4/ip.c: Don't try to forward link-local addresses
+
+ 2010-03-06: Simon Goldschmidt
+ * etharp.c: Fixed bug #29087: etharp: don't send packets for LinkLocal-
+ addresses to gw
+
+ 2010-03-05: Simon Goldschmidt
+ * dhcp.c: Fixed bug #29072: Correctly set ciaddr based on message-type
+ and state.
+
+ 2010-03-05: Simon Goldschmidt
+ * api_msg.c: Correctly set TCP_WRITE_FLAG_MORE when netconn_write is split
+ into multiple calls to tcp_write.
+
+ 2010-02-21: Simon Goldschmidt
+ * opt.h, mem.h, dns.c: task #10140: Remove DNS_USES_STATIC_BUF (keep
+ the implementation of DNS_USES_STATIC_BUF==1)
+
+ 2010-02-20: Simon Goldschmidt
+ * tcp.h, tcp.c, tcp_in.c, tcp_out.c: Task #10088: Correctly implement
+ close() vs. shutdown(). Now the application does not get any more
+ recv callbacks after calling tcp_close(). Added tcp_shutdown().
+
+ 2010-02-19: Simon Goldschmidt
+ * mem.c/.h, pbuf.c: Renamed mem_realloc() to mem_trim() to prevent
+ confusion with realloc()
+
+ 2010-02-15: Simon Goldschmidt/Stephane Lesage
+ * netif.c/.h: Link status does not depend on LWIP_NETIF_LINK_CALLBACK
+ (fixes bug #28899)
+
+ 2010-02-14: Simon Goldschmidt
+ * netif.c: Fixed bug #28877 (Duplicate ARP gratuitous packet with
+ LWIP_NETIF_LINK_CALLBACK set on) by only sending if both link- and
+ admin-status of a netif are up
+
+ 2010-02-14: Simon Goldschmidt
+ * opt.h: Disable ETHARP_TRUST_IP_MAC by default since it slows down packet
+ reception and is not really necessary
+
+ 2010-02-14: Simon Goldschmidt
+ * etharp.c/.h: Fixed ARP input processing: only add a new entry if a
+ request was directed as us (RFC 826, Packet Reception), otherwise
+ only update existing entries; internalized some functions
+
+ 2010-02-14: Simon Goldschmidt
+ * netif.h, etharp.c, tcpip.c: Fixed bug #28183 (ARP and TCP/IP cannot be
+ disabled on netif used for PPPoE) by adding a new netif flag
+ (NETIF_FLAG_ETHERNET) that tells the stack the device is an ethernet
+ device but prevents usage of ARP (so that ethernet_input can be used
+ for PPPoE).
+
+ 2010-02-12: Simon Goldschmidt
+ * netif.c: netif_set_link_up/down: only do something if the link state
+ actually changes
+
+ 2010-02-12: Simon Goldschmidt/Stephane Lesage
+ * api_msg.c: Fixed bug #28865 (Cannot close socket/netconn in non-blocking
+ connect)
+
+ 2010-02-12: Simon Goldschmidt
+ * mem.h: Fixed bug #28866 (mem_realloc function defined in mem.h)
+
+ 2010-02-09: Simon Goldschmidt
+ * api_lib.c, api_msg.c, sockets.c, api.h, api_msg.h: Fixed bug #22110
+ (recv() makes receive window update for data that wasn't received by
+ application)
+
+ 2010-02-09: Simon Goldschmidt/Stephane Lesage
+ * sockets.c: Fixed bug #28853 (lwip_recvfrom() returns 0 on receive time-out
+ or any netconn_recv() error)
+
+ 2010-02-09: Simon Goldschmidt
+ * ppp.c: task #10154 (PPP: Update snmp in/out counters for tx/rx packets)
+
+ 2010-02-09: Simon Goldschmidt
+ * netif.c: For loopback packets, adjust the stats- and snmp-counters
+ for the loopback netif.
+
+ 2010-02-08: Simon Goldschmidt
+ * igmp.c/.h, ip.h: Moved most defines from igmp.h to igmp.c for clarity
+ since they are not used anywhere else.
+
+ 2010-02-08: Simon Goldschmidt (Stéphane Lesage)
+ * igmp.c, igmp.h, stats.c, stats.h: Improved IGMP stats
+ (patch from bug #28798)
+
+ 2010-02-08: Simon Goldschmidt (Stéphane Lesage)
+ * igmp.c: Fixed bug #28798 (Error in "Max Response Time" processing) and
+ another bug when LWIP_RAND() returns zero.
+
+ 2010-02-04: Simon Goldschmidt
+ * nearly every file: Use macros defined in ip_addr.h (some of them new)
+ to work with IP addresses (preparation for bug #27352 - Change ip_addr
+ from struct to typedef (u32_t) - and better code).
+
+ 2010-01-31: Simon Goldschmidt
+ * netif.c: Don't call the link-callback from netif_set_up/down() since
+ this invalidly retriggers DHCP.
+
+ 2010-01-29: Simon Goldschmidt
+ * ip_addr.h, inet.h, def.h, inet.c, def.c, more: Cleanly separate the
+ portability file inet.h and its contents from the stack: moved htonX-
+ functions to def.h (and the new def.c - they are not ipv4 dependent),
+ let inet.h depend on ip_addr.h and not the other way round.
+ This fixes bug #28732.
+
+ 2010-01-28: Kieran Mansley
+ * tcp.c: Ensure ssthresh >= 2*MSS
+
+ 2010-01-27: Simon Goldschmidt
+ * tcp.h, tcp.c, tcp_in.c: Fixed bug #27871: Calling tcp_abort() in recv
+ callback can lead to accessing unallocated memory. As a consequence,
+ ERR_ABRT means the application has called tcp_abort()!
+
+ 2010-01-25: Simon Goldschmidt
+ * snmp_structs.h, msg_in.c: Partly fixed bug #22070 (MIB_OBJECT_WRITE_ONLY
+ not implemented in SNMP): write-only or not-accessible are still
+ returned by getnext (though not by get)
+
+ 2010-01-24: Simon Goldschmidt
+ * snmp: Renamed the private mib node from 'private' to 'mib_private' to
+ not use reserved C/C++ keywords
+
+ 2010-01-23: Simon Goldschmidt
+ * sockets.c: Fixed bug #28716: select() returns 0 after waiting for less
+ than 1 ms
+
+ 2010-01-21: Simon Goldschmidt
+ * tcp.c, api_msg.c: Fixed bug #28651 (tcp_connect: no callbacks called
+ if tcp_enqueue fails) both in raw- and netconn-API
+
+ 2010-01-19: Simon Goldschmidt
+ * api_msg.c: Fixed bug #27316: netconn: Possible deadlock in err_tcp
+
+ 2010-01-18: Iordan Neshev/Simon Goldschmidt
+ * src/netif/ppp: reorganised PPP sourcecode to 2.3.11 including some
+ bugfix backports from 2.4.x.
+
+ 2010-01-18: Simon Goldschmidt
+ * mem.c: Fixed bug #28679: mem_realloc calculates mem_stats wrong
+
+ 2010-01-17: Simon Goldschmidt
+ * api_lib.c, api_msg.c, (api_msg.h, api.h, sockets.c, tcpip.c):
+ task #10102: "netconn: clean up conn->err threading issues" by adding
+ error return value to struct api_msg_msg
+
+ 2010-01-17: Simon Goldschmidt
+ * api.h, api_lib.c, sockets.c: Changed netconn_recv() and netconn_accept()
+ to return err_t (bugs #27709 and #28087)
+
+ 2010-01-14: Simon Goldschmidt
+ * ...: Use typedef for function prototypes throughout the stack.
+
+ 2010-01-13: Simon Goldschmidt
+ * api_msg.h/.c, api_lib.c: Fixed bug #26672 (close connection when receive
+ window = 0) by correctly draining recvmbox/acceptmbox
+
+ 2010-01-11: Simon Goldschmidt
+ * pap.c: Fixed bug #13315 (PPP PAP authentication can result in
+ erroneous callbacks) by copying the code from recent pppd
+
+ 2010-01-10: Simon Goldschmidt
+ * raw.c: Fixed bug #28506 (raw_bind should filter received packets)
+
+ 2010-01-10: Simon Goldschmidt
+ * tcp.h/.c: bug #28127 (remove call to tcp_output() from tcp_ack(_now)())
+
+ 2010-01-08: Simon Goldschmidt
+ * sockets.c: Fixed bug #28519 (lwip_recvfrom bug with len > 65535)
+
+ 2010-01-08: Simon Goldschmidt
+ * dns.c: Copy hostname for DNS_LOCAL_HOSTLIST_IS_DYNAMIC==1 since string
+ passed to dns_local_addhost() might be volatile
+
+ 2010-01-07: Simon Goldschmidt
+ * timers.c, tcp.h: Call tcp_timer_needed() with NO_SYS==1, too
+
+ 2010-01-06: Simon Goldschmidt
+ * netdb.h: Fixed bug #28496: missing include guards in netdb.h
+
+ 2009-12-31: Simon Goldschmidt
+ * many ppp files: Reorganised PPP source code from ucip structure to pppd
+ structure to easily compare our code against the pppd code (around v2.3.1)
+
+ 2009-12-27: Simon Goldschmidt
+ * tcp_in.c: Another fix for bug #28241 (ooseq processing) and adapted
+ unit test
+
+
+(STABLE-1.3.2)
+
+ ++ New features:
+
+ 2009-10-27 Simon Goldschmidt/Stephan Lesage
+ * netifapi.c/.h: Added netifapi_netif_set_addr()
+
+ 2009-10-07 Simon Goldschmidt/Fabian Koch
+ * api_msg.c, netbuf.c/.h, opt.h: patch #6888: Patch for UDP Netbufs to
+ support dest-addr and dest-port (optional: LWIP_NETBUF_RECVINFO)
+
+ 2009-08-26 Simon Goldschmidt/Simon Kallweit
+ * slipif.c/.h: bug #26397: SLIP polling support
+
+ 2009-08-25 Simon Goldschmidt
+ * opt.h, etharp.h/.c: task #9033: Support IEEE 802.1q tagged frame (VLAN),
+ New configuration options ETHARP_SUPPORT_VLAN and ETHARP_VLAN_CHECK.
+
+ 2009-08-25 Simon Goldschmidt
+ * ip_addr.h, netdb.c: patch #6900: added define ip_ntoa(struct ip_addr*)
+
+ 2009-08-24 Jakob Stoklund Olesen
+ * autoip.c, dhcp.c, netif.c: patch #6725: Teach AutoIP and DHCP to respond
+ to netif_set_link_up().
+
+ 2009-08-23 Simon Goldschmidt
+ * tcp.h/.c: Added function tcp_debug_state_str() to convert a tcp state
+ to a human-readable string.
+
+ ++ Bugfixes:
+
+ 2009-12-24: Kieran Mansley
+ * tcp_in.c Apply patches from Oleg Tyshev to improve OOS processing
+ (BUG#28241)
+
+ 2009-12-06: Simon Goldschmidt
+ * ppp.h/.c: Fixed bug #27079 (Yet another leak in PPP): outpacket_buf can
+ be statically allocated (like in ucip)
+
+ 2009-12-04: Simon Goldschmidt (patch by Ioardan Neshev)
+ * pap.c: patch #6969: PPP: missing PAP authentication UNTIMEOUT
+
+ 2009-12-03: Simon Goldschmidt
+ * tcp.h, tcp_in.c, tcp_out.c: Fixed bug #28106: dup ack for fast retransmit
+ could have non-zero length
+
+ 2009-12-02: Simon Goldschmidt
+ * tcp_in.c: Fixed bug #27904: TCP sends too many ACKs: delay resetting
+ tcp_input_pcb until after calling the pcb's callbacks
+
+ 2009-11-29: Simon Goldschmidt
+ * tcp_in.c: Fixed bug #28054: Two segments with FIN flag on the out-of-
+ sequence queue, also fixed PBUF_POOL leak in the out-of-sequence code
+
+ 2009-11-29: Simon Goldschmidt
+ * pbuf.c: Fixed bug #28064: pbuf_alloc(PBUF_POOL) is not thread-safe by
+ queueing a call into tcpip_thread to free ooseq-bufs if the pool is empty
+
+ 2009-11-26: Simon Goldschmidt
+ * tcp.h: Fixed bug #28098: Nagle can prevent fast retransmit from sending
+ segment
+
+ 2009-11-26: Simon Goldschmidt
+ * tcp.h, sockets.c: Fixed bug #28099: API required to disable Nagle
+ algorithm at PCB level
+
+ 2009-11-22: Simon Goldschmidt
+ * tcp_out.c: Fixed bug #27905: FIN isn't combined with data on unsent
+
+ 2009-11-22: Simon Goldschmidt (suggested by Bill Auerbach)
+ * tcp.c: tcp_alloc: prevent increasing stats.err for MEMP_TCP_PCB when
+ reusing time-wait pcb
+
+ 2009-11-20: Simon Goldschmidt (patch by Albert Bartel)
+ * sockets.c: Fixed bug #28062: Data received directly after accepting
+ does not wake up select
+
+ 2009-11-11: Simon Goldschmidt
+ * netdb.h: Fixed bug #27994: incorrect define for freeaddrinfo(addrinfo)
+
+ 2009-10-30: Simon Goldschmidt
+ * opt.h: Increased default value for TCP_MSS to 536, updated default
+ value for TCP_WND to 4*TCP_MSS to keep delayed ACK working.
+
+ 2009-10-28: Kieran Mansley
+ * tcp_in.c, tcp_out.c, tcp.h: re-work the fast retransmission code
+ to follow algorithm from TCP/IP Illustrated
+
+ 2009-10-27: Kieran Mansley
+ * tcp_in.c: fix BUG#27445: grow cwnd with every duplicate ACK
+
+ 2009-10-25: Simon Goldschmidt
+ * tcp.h: bug-fix in the TCP_EVENT_RECV macro (has to call tcp_recved if
+ pcb->recv is NULL to keep rcv_wnd correct)
+
+ 2009-10-25: Simon Goldschmidt
+ * tcp_in.c: Fixed bug #26251: RST process in TIME_WAIT TCP state
+
+ 2009-10-23: Simon Goldschmidt (David Empson)
+ * tcp.c: Fixed bug #27783: Silly window avoidance for small window sizes
+
+ 2009-10-21: Simon Goldschmidt
+ * tcp_in.c: Fixed bug #27215: TCP sent() callback gives leading and
+ trailing 1 byte len (SYN/FIN)
+
+ 2009-10-21: Simon Goldschmidt
+ * tcp_out.c: Fixed bug #27315: zero window probe and FIN
+
+ 2009-10-19: Simon Goldschmidt
+ * dhcp.c/.h: Minor code simplification (don't store received pbuf, change
+ conditional code to assert where applicable), check pbuf length before
+ testing for valid reply
+
+ 2009-10-19: Simon Goldschmidt
+ * dhcp.c: Removed most calls to udp_connect since they aren't necessary
+ when using udp_sendto_if() - always stay connected to IP_ADDR_ANY.
+
+ 2009-10-16: Simon Goldschmidt
+ * ip.c: Fixed bug #27390: Source IP check in ip_input() causes it to drop
+ valid DHCP packets -> allow 0.0.0.0 as source address when LWIP_DHCP is
+ enabled
+
+ 2009-10-15: Simon Goldschmidt (Oleg Tyshev)
+ * tcp_in.c: Fixed bug #27329: dupacks by unidirectional data transmit
+
+ 2009-10-15: Simon Goldschmidt
+ * api_lib.c: Fixed bug #27709: conn->err race condition on netconn_recv()
+ timeout
+
+ 2009-10-15: Simon Goldschmidt
+ * autoip.c: Fixed bug #27704: autoip starts with wrong address
+ LWIP_AUTOIP_CREATE_SEED_ADDR() returned address in host byte order instead
+ of network byte order
+
+ 2009-10-11 Simon Goldschmidt (Jörg Kesten)
+ * tcp_out.c: Fixed bug #27504: tcp_enqueue wrongly concatenates segments
+ which are not consecutive when retransmitting unacked segments
+
+ 2009-10-09 Simon Goldschmidt
+ * opt.h: Fixed default values of some stats to only be enabled if used
+ Fixes bug #27338: sys_stats is defined when NO_SYS = 1
+
+ 2009-08-30 Simon Goldschmidt
+ * ip.c: Fixed bug bug #27345: "ip_frag() does not use the LWIP_NETIF_LOOPBACK
+ function" by checking for loopback before calling ip_frag
+
+ 2009-08-25 Simon Goldschmidt
+ * dhcp.c: fixed invalid dependency to etharp_query if DHCP_DOES_ARP_CHECK==0
+
+ 2009-08-23 Simon Goldschmidt
+ * ppp.c: bug #27078: Possible memory leak in pppInit()
+
+ 2009-08-23 Simon Goldschmidt
+ * netdb.c, dns.c: bug #26657: DNS, if host name is "localhost", result
+ is error.
+
+ 2009-08-23 Simon Goldschmidt
+ * opt.h, init.c: bug #26649: TCP fails when TCP_MSS > TCP_SND_BUF
+ Fixed wrong parenthesis, added check in init.c
+
+ 2009-08-23 Simon Goldschmidt
+ * ppp.c: bug #27266: wait-state debug message in pppMain occurs every ms
+
+ 2009-08-23 Simon Goldschmidt
+ * many ppp files: bug #27267: Added include to string.h where needed
+
+ 2009-08-23 Simon Goldschmidt
+ * tcp.h: patch #6843: tcp.h macro optimization patch (for little endian)
+
+
+(STABLE-1.3.1)
+
+ ++ New features:
+
+ 2009-05-10 Simon Goldschmidt
+ * opt.h, sockets.c, pbuf.c, netbuf.h, pbuf.h: task #7013: Added option
+ LWIP_NETIF_TX_SINGLE_PBUF to try to create transmit packets from only
+ one pbuf to help MACs that don't support scatter-gather DMA.
+
+ 2009-05-09 Simon Goldschmidt
+ * icmp.h, icmp.c: Shrinked ICMP code, added option to NOT check icoming
+ ECHO pbuf for size (just use it): LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN
+
+ 2009-05-05 Simon Goldschmidt, Jakob Stoklund Olesen
+ * ip.h, ip.c: Added ip_current_netif() & ip_current_header() to receive
+ extended info about the currently received packet.
+
+ 2009-04-27 Simon Goldschmidt
+ * sys.h: Made SYS_LIGHTWEIGHT_PROT and sys_now() work with NO_SYS=1
+
+ 2009-04-25 Simon Goldschmidt
+ * mem.c, opt.h: Added option MEM_USE_POOLS_TRY_BIGGER_POOL to try the next
+ bigger malloc pool if one is empty (only usable with MEM_USE_POOLS).
+
+ 2009-04-21 Simon Goldschmidt
+ * dns.c, init.c, dns.h, opt.h: task #7507, patch #6786: DNS supports static
+ hosts table. New configuration options DNS_LOCAL_HOSTLIST and
+ DNS_LOCAL_HOSTLIST_IS_DYNAMIC. Also, DNS_LOOKUP_LOCAL_EXTERN() can be defined
+ as an external function for lookup.
+
+ 2009-04-15 Simon Goldschmidt
+ * dhcp.c: patch #6763: Global DHCP XID can be redefined to something more unique
+
+ 2009-03-31 Kieran Mansley
+ * tcp.c, tcp_out.c, tcp_in.c, sys.h, tcp.h, opts.h: add support for
+ TCP timestamp options, off by default. Rework tcp_enqueue() to
+ take option flags rather than specified option data
+
+ 2009-02-18 Simon Goldschmidt
+ * cc.h: Added printf formatter for size_t: SZT_F
+
+ 2009-02-16 Simon Goldschmidt (patch by Rishi Khan)
+ * icmp.c, opt.h: patch #6539: (configurable) response to broadcast- and multicast
+ pings
+
+ 2009-02-12 Simon Goldschmidt
+ * init.h: Added LWIP_VERSION to get the current version of the stack
+
+ 2009-02-11 Simon Goldschmidt (suggested by Gottfried Spitaler)
+ * opt.h, memp.h/.c: added MEMP_MEM_MALLOC to use mem_malloc/mem_free instead
+ of the pool allocator (can save code size with MEM_LIBC_MALLOC if libc-malloc
+ is otherwise used)
+
+ 2009-01-28 Jonathan Larmour (suggested by Bill Bauerbach)
+ * ipv4/inet_chksum.c, ipv4/lwip/inet_chksum.h: inet_chksum_pseudo_partial()
+ is only used by UDPLITE at present, so conditionalise it.
+
+ 2008-12-03 Simon Goldschmidt (base on patch from Luca Ceresoli)
+ * autoip.c: checked in (slightly modified) patch #6683: Customizable AUTOIP
+ "seed" address. This should reduce AUTOIP conflicts if
+ LWIP_AUTOIP_CREATE_SEED_ADDR is overridden.
+
+ 2008-10-02 Jonathan Larmour and Rishi Khan
+ * sockets.c (lwip_accept): Return EWOULDBLOCK if would block on non-blocking
+ socket.
+
+ 2008-06-30 Simon Goldschmidt
+ * mem.c, opt.h, stats.h: fixed bug #21433: Calling mem_free/pbuf_free from
+ interrupt context isn't safe: LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT allows
+ mem_free to run between mem_malloc iterations. Added illegal counter for
+ mem stats.
+
+ 2008-06-27 Simon Goldschmidt
+ * stats.h/.c, some other files: patch #6483: stats module improvement:
+ Added defines to display each module's statistic individually, added stats
+ defines for MEM, MEMP and SYS modules, removed (unused) rexmit counter.
+
+ 2008-06-17 Simon Goldschmidt
+ * err.h: patch #6459: Made err_t overridable to use a more efficient type
+ (define LWIP_ERR_T in cc.h)
+
+ 2008-06-17 Simon Goldschmidt
+ * slipif.c: patch #6480: Added a configuration option for slipif for symmetry
+ to loopif
+
+ 2008-06-17 Simon Goldschmidt (patch by Luca Ceresoli)
+ * netif.c, loopif.c, ip.c, netif.h, loopif.h, opt.h: Checked in slightly
+ modified version of patch # 6370: Moved loopif code to netif.c so that
+ loopback traffic is supported on all netifs (all local IPs).
+ Added option to limit loopback packets for each netifs.
+
+
+ ++ Bugfixes:
+ 2009-08-12 Kieran Mansley
+ * tcp_in.c, tcp.c: Fix bug #27209: handle trimming of segments when
+ out of window or out of order properly
+
+ 2009-08-12 Kieran Mansley
+ * tcp_in.c: Fix bug #27199: use snd_wl2 instead of snd_wl1
+
+ 2009-07-28 Simon Goldschmidt
+ * mem.h: Fixed bug #27105: "realloc() cannot replace mem_realloc()"s
+
+ 2009-07-27 Kieran Mansley
+ * api.h api_msg.h netdb.h sockets.h: add missing #include directives
+
+ 2009-07-09 Kieran Mansley
+ * api_msg.c, sockets.c, api.h: BUG23240 use signed counters for
+ recv_avail and don't increment counters until message successfully
+ sent to mbox
+
+ 2009-06-25 Kieran Mansley
+ * api_msg.c api.h: BUG26722: initialise netconn write variables
+ in netconn_alloc
+
+ 2009-06-25 Kieran Mansley
+ * tcp.h: BUG26879: set ret value in TCP_EVENT macros when function is not set
+
+ 2009-06-25 Kieran Mansley
+ * tcp.c, tcp_in.c, tcp_out.c, tcp.h: BUG26301 and BUG26267: correct
+ simultaneous close behaviour, and make snd_nxt have the same meaning
+ as in the RFCs.
+
+ 2009-05-12 Simon Goldschmidt
+ * etharp.h, etharp.c, netif.c: fixed bug #26507: "Gratuitous ARP depends on
+ arp_table / uses etharp_query" by adding etharp_gratuitous()
+
+ 2009-05-12 Simon Goldschmidt
+ * ip.h, ip.c, igmp.c: bug #26487: Added ip_output_if_opt that can add IP options
+ to the IP header (used by igmp_ip_output_if)
+
+ 2009-05-06 Simon Goldschmidt
+ * inet_chksum.c: On little endian architectures, use LWIP_PLATFORM_HTONS (if
+ defined) for SWAP_BYTES_IN_WORD to speed up checksumming.
+
+ 2009-05-05 Simon Goldschmidt
+ * sockets.c: bug #26405: Prematurely released semaphore causes lwip_select()
+ to crash
+
+ 2009-05-04 Simon Goldschmidt
+ * init.c: snmp was not initialized in lwip_init()
+
+ 2009-05-04 Frédéric Bernon
+ * dhcp.c, netbios.c: Changes if IP_SOF_BROADCAST is enabled.
+
+ 2009-05-03 Simon Goldschmidt
+ * tcp.h: bug #26349: Nagle algorithm doesn't send although segment is full
+ (and unsent->next == NULL)
+
+ 2009-05-02 Simon Goldschmidt
+ * tcpip.h, tcpip.c: fixed tcpip_untimeout (does not need the time, broken after
+ 1.3.0 in CVS only) - fixes compilation of ppp_oe.c
+
+ 2009-05-02 Simon Goldschmidt
+ * msg_in.c: fixed bug #25636: SNMPSET value is ignored for integer fields
+
+ 2009-05-01 Simon Goldschmidt
+ * pap.c: bug #21680: PPP upap_rauthnak() drops legal NAK packets
+
+ 2009-05-01 Simon Goldschmidt
+ * ppp.c: bug #24228: Memory corruption with PPP and DHCP
+
+ 2009-04-29 Frédéric Bernon
+ * raw.c, udp.c, init.c, opt.h, ip.h, sockets.h: bug #26309: Implement the
+ SO(F)_BROADCAST filter for all API layers. Avoid the unindented reception
+ of broadcast packets even when this option wasn't set. Port maintainers
+ which want to enable this filter have to set IP_SOF_BROADCAST=1 in opt.h.
+ If you want this option also filter broadcast on recv operations, you also
+ have to set IP_SOF_BROADCAST_RECV=1 in opt.h.
+
+ 2009-04-28 Simon Goldschmidt, Jakob Stoklund Olesen
+ * dhcp.c: patch #6721, bugs #25575, #25576: Some small fixes to DHCP and
+ DHCP/AUTOIP cooperation
+
+ 2009-04-25 Simon Goldschmidt, Oleg Tyshev
+ * tcp_out.c: bug #24212: Deadlocked tcp_retransmit due to exceeded pcb->cwnd
+ Fixed by sorting the unsent and unacked queues (segments are inserted at the
+ right place in tcp_output and tcp_rexmit).
+
+ 2009-04-25 Simon Goldschmidt
+ * memp.c, mem.c, memp.h, mem_std.h: bug #26213 "Problem with memory allocation
+ when debugging": memp_sizes contained the wrong sizes (including sanity
+ regions); memp pools for MEM_USE_POOLS were too small
+
+ 2009-04-24 Simon Goldschmidt, Frédéric Bernon
+ * inet.c: patch #6765: Fix a small problem with the last changes (incorrect
+ behavior, with with ip address string not ended by a '\0', a space or a
+ end of line)
+
+ 2009-04-19 Simon Goldschmidt
+ * rawapi.txt: Fixed bug #26069: Corrected documentation: if tcp_connect fails,
+ pcb->err is called, not pcb->connected (with an error code).
+
+ 2009-04-19 Simon Goldschmidt
+ * tcp_out.c: Fixed bug #26236: "TCP options (timestamp) don't work with
+ no-copy-tcpwrite": deallocate option data, only concat segments with same flags
+
+ 2009-04-19 Simon Goldschmidt
+ * tcp_out.c: Fixed bug #25094: "Zero-length pbuf" (options are now allocated
+ in the header pbuf, not the data pbuf)
+
+ 2009-04-18 Simon Goldschmidt
+ * api_msg.c: fixed bug #25695: Segmentation fault in do_writemore()
+
+ 2009-04-15 Simon Goldschmidt
+ * sockets.c: tried to fix bug #23559: lwip_recvfrom problem with tcp
+
+ 2009-04-15 Simon Goldschmidt
+ * dhcp.c: task #9192: mem_free of dhcp->options_in and dhcp->msg_in
+
+ 2009-04-15 Simon Goldschmidt
+ * ip.c, ip6.c, tcp_out.c, ip.h: patch #6808: Add a utility function
+ ip_hinted_output() (for smaller code mainly)
+
+ 2009-04-15 Simon Goldschmidt
+ * inet.c: patch #6765: Supporting new line characters in inet_aton()
+
+ 2009-04-15 Simon Goldschmidt
+ * dhcp.c: patch #6764: DHCP rebind and renew did not send hostnam option;
+ Converted constant OPTION_MAX_MSG_SIZE to netif->mtu, check if netif->mtu
+ is big enough in dhcp_start
+
+ 2009-04-15 Simon Goldschmidt
+ * netbuf.c: bug #26027: netbuf_chain resulted in pbuf memory leak
+
+ 2009-04-15 Simon Goldschmidt
+ * sockets.c, ppp.c: bug #25763: corrected 4 occurrences of SMEMCPY to MEMCPY
+
+ 2009-04-15 Simon Goldschmidt
+ * sockets.c: bug #26121: set_errno can be overridden
+
+ 2009-04-09 Kieran Mansley (patch from Luca Ceresoli <lucaceresoli>)
+ * init.c, opt.h: Patch#6774 TCP_QUEUE_OOSEQ breaks compilation when
+ LWIP_TCP==0
+
+ 2009-04-09 Kieran Mansley (patch from Roy Lee <roylee17>)
+ * tcp.h: Patch#6802 Add do-while-clauses to those function like
+ macros in tcp.h
+
+ 2009-03-31 Kieran Mansley
+ * tcp.c, tcp_in.c, tcp_out.c, tcp.h, opt.h: Rework the way window
+ updates are calculated and sent (BUG20515)
+
+ * tcp_in.c: cope with SYN packets received during established states,
+ and retransmission of initial SYN.
+
+ * tcp_out.c: set push bit correctly when tcp segments are merged
+
+ 2009-03-27 Kieran Mansley
+ * tcp_out.c set window correctly on probes (correcting change made
+ yesterday)
+
+ 2009-03-26 Kieran Mansley
+ * tcp.c, tcp_in.c, tcp.h: add tcp_abandon() to cope with dropping
+ connections where no reset required (bug #25622)
+
+ * tcp_out.c: set TCP_ACK flag on keepalive and zero window probes
+ (bug #20779)
+
+ 2009-02-18 Simon Goldschmidt (Jonathan Larmour and Bill Auerbach)
+ * ip_frag.c: patch #6528: the buffer used for IP_FRAG_USES_STATIC_BUF could be
+ too small depending on MEM_ALIGNMENT
+
+ 2009-02-16 Simon Goldschmidt
+ * sockets.h/.c, api_*.h/.c: fixed arguments of socket functions to match the standard;
+ converted size argument of netconn_write to 'size_t'
+
+ 2009-02-16 Simon Goldschmidt
+ * tcp.h, tcp.c: fixed bug #24440: TCP connection close problem on 64-bit host
+ by moving accept callback function pointer to TCP_PCB_COMMON
+
+ 2009-02-12 Simon Goldschmidt
+ * dhcp.c: fixed bug #25345 (DHCPDECLINE is sent with "Maximum message size"
+ option)
+
+ 2009-02-11 Simon Goldschmidt
+ * dhcp.c: fixed bug #24480 (releasing old udp_pdb and pbuf in dhcp_start)
+
+ 2009-02-11 Simon Goldschmidt
+ * opt.h, api_msg.c: added configurable default valud for netconn->recv_bufsize:
+ RECV_BUFSIZE_DEFAULT (fixes bug #23726: pbuf pool exhaustion on slow recv())
+
+ 2009-02-10 Simon Goldschmidt
+ * tcp.c: fixed bug #25467: Listen backlog is not reset on timeout in SYN_RCVD:
+ Accepts_pending is decrease on a corresponding listen pcb when a connection
+ in state SYN_RCVD is close.
+
+ 2009-01-28 Jonathan Larmour
+ * pbuf.c: reclaim pbufs from TCP out-of-sequence segments if we run
+ out of pool pbufs.
+
+ 2008-12-19 Simon Goldschmidt
+ * many files: patch #6699: fixed some warnings on platform where sizeof(int) == 2
+
+ 2008-12-10 Tamas Somogyi, Frédéric Bernon
+ * sockets.c: fixed bug #25051: lwip_recvfrom problem with udp: fromaddr and
+ port uses deleted netbuf.
+
+ 2008-10-18 Simon Goldschmidt
+ * tcp_in.c: fixed bug ##24596: Vulnerability on faulty TCP options length
+ in tcp_parseopt
+
+ 2008-10-15 Simon Goldschmidt
+ * ip_frag.c: fixed bug #24517: IP reassembly crashes on unaligned IP headers
+ by packing the struct ip_reass_helper.
+
+ 2008-10-03 David Woodhouse, Jonathan Larmour
+ * etharp.c (etharp_arp_input): Fix type aliasing problem copying ip address.
+
+ 2008-10-02 Jonathan Larmour
+ * dns.c: Hard-code structure sizes, to avoid issues on some compilers where
+ padding is included.
+
+ 2008-09-30 Jonathan Larmour
+ * sockets.c (lwip_accept): check addr isn't NULL. If it's valid, do an
+ assertion check that addrlen isn't NULL.
+
+ 2008-09-30 Jonathan Larmour
+ * tcp.c: Fix bug #24227, wrong error message in tcp_bind.
+
+ 2008-08-26 Simon Goldschmidt
+ * inet.h, ip_addr.h: fixed bug #24132: Cross-dependency between ip_addr.h and
+ inet.h -> moved declaration of struct in_addr from ip_addr.h to inet.h
+
+ 2008-08-14 Simon Goldschmidt
+ * api_msg.c: fixed bug #23847: do_close_internal references freed memory (when
+ tcp_close returns != ERR_OK)
+
+ 2008-07-08 Frédéric Bernon
+ * stats.h: Fix some build bugs introduced with patch #6483 (missing some parameters
+ in macros, mainly if MEM_STATS=0 and MEMP_STATS=0).
+
+ 2008-06-24 Jonathan Larmour
+ * tcp_in.c: Fix for bug #23693 as suggested by Art R. Ensure cseg is unused
+ if tcp_seg_copy fails.
+
+ 2008-06-17 Simon Goldschmidt
+ * inet_chksum.c: Checked in some ideas of patch #6460 (loop optimizations)
+ and created defines for swapping bytes and folding u32 to u16.
+
+ 2008-05-30 Kieran Mansley
+ * tcp_in.c Remove redundant "if" statement, and use real rcv_wnd
+ rather than rcv_ann_wnd when deciding if packets are in-window.
+ Contributed by <arasmussen@consultant.datasys.swri.edu>
+
+ 2008-05-30 Kieran Mansley
+ * mem.h: Fix BUG#23254. Change macro definition of mem_* to allow
+ passing as function pointers when MEM_LIBC_MALLOC is defined.
+
+ 2008-05-09 Jonathan Larmour
+ * err.h, err.c, sockets.c: Fix bug #23119: Reorder timeout error code to
+ stop it being treated as a fatal error.
+
+ 2008-04-15 Simon Goldschmidt
+ * dhcp.c: fixed bug #22804: dhcp_stop doesn't clear NETIF_FLAG_DHCP
+ (flag now cleared)
+
+ 2008-03-27 Simon Goldschmidt
+ * mem.c, tcpip.c, tcpip.h, opt.h: fixed bug #21433 (Calling mem_free/pbuf_free
+ from interrupt context isn't safe): set LWIP_USE_HEAP_FROM_INTERRUPT to 1
+ in lwipopts.h or use pbuf_free_callback(p)/mem_free_callback(m) to free pbufs
+ or heap memory from interrupt context
+
+ 2008-03-26 Simon Goldschmidt
+ * tcp_in.c, tcp.c: fixed bug #22249: division by zero could occur if a remote
+ host sent a zero mss as TCP option.
+
+
+(STABLE-1.3.0)
+
+ ++ New features:
+
+ 2008-03-10 Jonathan Larmour
+ * inet_chksum.c: Allow choice of one of the sample algorithms to be
+ made from lwipopts.h. Fix comment on how to override LWIP_CHKSUM.
+
+ 2008-01-22 Frédéric Bernon
+ * tcp.c, tcp_in.c, tcp.h, opt.h: Rename LWIP_CALCULATE_EFF_SEND_MSS in
+ TCP_CALCULATE_EFF_SEND_MSS to have coherent TCP options names.
+
+ 2008-01-14 Frédéric Bernon
+ * rawapi.txt, api_msg.c, tcp.c, tcp_in.c, tcp.h: changes for task #7675 "Enable
+ to refuse data on a TCP_EVENT_RECV call". Important, behavior changes for the
+ tcp_recv callback (see rawapi.txt).
+
+ 2008-01-14 Frédéric Bernon, Marc Chaland
+ * ip.c: Integrate patch #6369" ip_input : checking before realloc".
+
+ 2008-01-12 Frédéric Bernon
+ * tcpip.h, tcpip.c, api.h, api_lib.c, api_msg.c, sockets.c: replace the field
+ netconn::sem per netconn::op_completed like suggested for the task #7490
+ "Add return value to sys_mbox_post".
+
+ 2008-01-12 Frédéric Bernon
+ * api_msg.c, opt.h: replace DEFAULT_RECVMBOX_SIZE per DEFAULT_TCP_RECVMBOX_SIZE,
+ DEFAULT_UDP_RECVMBOX_SIZE and DEFAULT_RAW_RECVMBOX_SIZE (to optimize queues
+ sizes), like suggested for the task #7490 "Add return value to sys_mbox_post".
+
+ 2008-01-10 Frédéric Bernon
+ * tcpip.h, tcpip.c: add tcpip_callback_with_block function for the task #7490
+ "Add return value to sys_mbox_post". tcpip_callback is always defined as
+ "blocking" ("block" parameter = 1).
+
+ 2008-01-10 Frédéric Bernon
+ * tcpip.h, tcpip.c, api.h, api_lib.c, api_msg.c, sockets.c: replace the field
+ netconn::mbox (sys_mbox_t) per netconn::sem (sys_sem_t) for the task #7490
+ "Add return value to sys_mbox_post".
+
+ 2008-01-05 Frédéric Bernon
+ * sys_arch.txt, api.h, api_lib.c, api_msg.h, api_msg.c, tcpip.c, sys.h, opt.h:
+ Introduce changes for task #7490 "Add return value to sys_mbox_post" with some
+ modifications in the sys_mbox api: sys_mbox_new take a "size" parameters which
+ indicate the number of pointers query by the mailbox. There is three defines
+ in opt.h to indicate sizes for tcpip::mbox, netconn::recvmbox, and for the
+ netconn::acceptmbox. Port maintainers, you can decide to just add this new
+ parameter in your implementation, but to ignore it to keep the previous behavior.
+ The new sys_mbox_trypost function return a value to know if the mailbox is
+ full or if the message is posted. Take a look to sys_arch.txt for more details.
+ This new function is used in tcpip_input (so, can be called in an interrupt
+ context since the function is not blocking), and in recv_udp and recv_raw.
+
+ 2008-01-04 Frédéric Bernon, Simon Goldschmidt, Jonathan Larmour
+ * rawapi.txt, api.h, api_lib.c, api_msg.h, api_msg.c, sockets.c, tcp.h, tcp.c,
+ tcp_in.c, init.c, opt.h: rename backlog options with TCP_ prefix, limit the
+ "backlog" parameter in an u8_t, 0 is interpreted as "smallest queue", add
+ documentation in the rawapi.txt file.
+
+ 2007-12-31 Kieran Mansley (based on patch from Per-Henrik Lundbolm)
+ * tcp.c, tcp_in.c, tcp_out.c, tcp.h: Add TCP persist timer
+
+ 2007-12-31 Frédéric Bernon, Luca Ceresoli
+ * autoip.c, etharp.c: ip_addr.h: Integrate patch #6348: "Broadcast ARP packets
+ in autoip". The change in etharp_raw could be removed, since all calls to
+ etharp_raw use ethbroadcast for the "ethdst_addr" parameter. But it could be
+ wrong in the future.
+
+ 2007-12-30 Frédéric Bernon, Tom Evans
+ * ip.c: Fix bug #21846 "LwIP doesn't appear to perform any IP Source Address
+ Filtering" reported by Tom Evans.
+
+ 2007-12-21 Frédéric Bernon, Simon Goldschmidt, Jonathan Larmour
+ * tcp.h, opt.h, api.h, api_msg.h, tcp.c, tcp_in.c, api_lib.c, api_msg.c,
+ sockets.c, init.c: task #7252: Implement TCP listen backlog: Warning: raw API
+ applications have to call 'tcp_accepted(pcb)' in their accept callback to
+ keep accepting new connections.
+
+ 2007-12-13 Frédéric Bernon
+ * api_msg.c, err.h, err.c, sockets.c, dns.c, dns.h: replace "enum dns_result"
+ by err_t type. Add a new err_t code "ERR_INPROGRESS".
+
+ 2007-12-12 Frédéric Bernon
+ * dns.h, dns.c, opt.h: move DNS options to the "right" place. Most visibles
+ are the one which have ram usage.
+
+ 2007-12-05 Frédéric Bernon
+ * netdb.c: add a LWIP_DNS_API_HOSTENT_STORAGE option to decide to use a static
+ set of variables (=0) or a local one (=1). In this last case, your port should
+ provide a function "struct hostent* sys_thread_hostent( struct hostent* h)"
+ which have to do a copy of "h" and return a pointer ont the "per-thread" copy.
+
+ 2007-12-03 Simon Goldschmidt
+ * ip.c: ip_input: check if a packet is for inp first before checking all other
+ netifs on netif_list (speeds up packet receiving in most cases)
+
+ 2007-11-30 Simon Goldschmidt
+ * udp.c, raw.c: task #7497: Sort lists (pcb, netif, ...) for faster access
+ UDP: move a (connected) pcb selected for input to the front of the list of
+ pcbs so that it is found faster next time. Same for RAW pcbs that have eaten
+ a packet.
+
+ 2007-11-28 Simon Goldschmidt
+ * etharp.c, stats.c, stats.h, opt.h: Introduced ETHARP_STATS
+
+ 2007-11-25 Simon Goldschmidt
+ * dhcp.c: dhcp_unfold_reply() uses pbuf_copy_partial instead of its own copy
+ algorithm.
+
+ 2007-11-24 Simon Goldschmidt
+ * netdb.h, netdb.c, sockets.h/.c: Moved lwip_gethostbyname from sockets.c
+ to the new file netdb.c; included lwip_getaddrinfo.
+
+ 2007-11-21 Simon Goldschmidt
+ * tcp.h, opt.h, tcp.c, tcp_in.c: implemented calculating the effective send-mss
+ based on the MTU of the netif used to send. Enabled by default. Disable by
+ setting LWIP_CALCULATE_EFF_SEND_MSS to 0. This fixes bug #21492.
+
+ 2007-11-19 Frédéric Bernon
+ * api_msg.c, dns.h, dns.c: Implement DNS_DOES_NAME_CHECK option (check if name
+ received match the name query), implement DNS_USES_STATIC_BUF (the place where
+ copy dns payload to parse the response), return an error if there is no place
+ for a new query, and fix some minor problems.
+
+ 2007-11-16 Simon Goldschmidt
+ * new files: ipv4/inet.c, ipv4/inet_chksum.c, ipv6/inet6.c
+ removed files: core/inet.c, core/inet6.c
+ Moved inet files into ipv4/ipv6 directory; splitted inet.c/inet.h into
+ inet and chksum part; changed includes in all lwIP files as appropriate
+
+ 2007-11-16 Simon Goldschmidt
+ * api.h, api_msg.h, api_lib.c, api_msg.c, socket.h, socket.c: Added sequential
+ dns resolver function for netconn api (netconn_gethostbyname) and socket api
+ (gethostbyname/gethostbyname_r).
+
+ 2007-11-15 Jim Pettinato, Frédéric Bernon
+ * opt.h, init.c, tcpip.c, dhcp.c, dns.h, dns.c: add DNS client for simple name
+ requests with RAW api interface. Initialization is done in lwip_init() with
+ build time options. DNS timer is added in tcpip_thread context. DHCP can set
+ DNS server ip addresses when options are received. You need to set LWIP_DNS=1
+ in your lwipopts.h file (LWIP_DNS=0 in opt.h). DNS_DEBUG can be set to get
+ some traces with LWIP_DEBUGF. Sanity check have been added. There is a "todo"
+ list with points to improve.
+
+ 2007-11-06 Simon Goldschmidt
+ * opt.h, mib2.c: Patch #6215: added ifAdminStatus write support (if explicitly
+ enabled by defining SNMP_SAFE_REQUESTS to 0); added code to check link status
+ for ifOperStatus if LWIP_NETIF_LINK_CALLBACK is defined.
+
+ 2007-11-06 Simon Goldschmidt
+ * api.h, api_msg.h and dependent files: Task #7410: Removed the need to include
+ core header files in api.h (ip/tcp/udp/raw.h) to hide the internal
+ implementation from netconn api applications.
+
+ 2007-11-03 Frédéric Bernon
+ * api.h, api_lib.c, api_msg.c, sockets.c, opt.h: add SO_RCVBUF option for UDP &
+ RAW netconn. You need to set LWIP_SO_RCVBUF=1 in your lwipopts.h (it's disabled
+ by default). Netconn API users can use the netconn_recv_bufsize macro to access
+ it. This is a first release which have to be improve for TCP. Note it used the
+ netconn::recv_avail which need to be more "thread-safe" (note there is already
+ the problem for FIONREAD with lwip_ioctl/ioctlsocket).
+
+ 2007-11-01 Frédéric Bernon, Marc Chaland
+ * sockets.h, sockets.c, api.h, api_lib.c, api_msg.h, api_msg.c, tcp.h, tcp_out.c:
+ Integrate "patch #6250 : MSG_MORE flag for send". MSG_MORE is used at socket api
+ layer, NETCONN_MORE at netconn api layer, and TCP_WRITE_FLAG_MORE at raw api
+ layer. This option enable to delayed TCP PUSH flag on multiple "write" calls.
+ Note that previous "copy" parameter for "write" APIs is now called "apiflags".
+
+ 2007-10-24 Frédéric Bernon
+ * api.h, api_lib.c, api_msg.c: Add macro API_EVENT in the same spirit than
+ TCP_EVENT_xxx macros to get a code more readable. It could also help to remove
+ some code (like we have talk in "patch #5919 : Create compile switch to remove
+ select code"), but it could be done later.
+
+ 2007-10-08 Simon Goldschmidt
+ * many files: Changed initialization: many init functions are not needed any
+ more since we now rely on the compiler initializing global and static
+ variables to zero!
+
+ 2007-10-06 Simon Goldschmidt
+ * ip_frag.c, memp.c, mib2.c, ip_frag.h, memp_std.h, opt.h: Changed IP_REASSEMBLY
+ to enqueue the received pbufs so that multiple packets can be reassembled
+ simultaneously and no static reassembly buffer is needed.
+
+ 2007-10-05 Simon Goldschmidt
+ * tcpip.c, etharp.h, etharp.c: moved ethernet_input from tcpip.c to etharp.c so
+ all netifs (or ports) can use it.
+
+ 2007-10-05 Frédéric Bernon
+ * netifapi.h, netifapi.c: add function netifapi_netif_set_default. Change the
+ common function to reduce a little bit the footprint (for all functions using
+ only the "netif" parameter).
+
+ 2007-10-03 Frédéric Bernon
+ * netifapi.h, netifapi.c: add functions netifapi_netif_set_up, netifapi_netif_set_down,
+ netifapi_autoip_start and netifapi_autoip_stop. Use a common function to reduce
+ a little bit the footprint (for all functions using only the "netif" parameter).
+
+ 2007-09-15 Frédéric Bernon
+ * udp.h, udp.c, sockets.c: Changes for "#20503 IGMP Improvement". Add IP_MULTICAST_IF
+ option in socket API, and a new field "multicast_ip" in "struct udp_pcb" (for
+ netconn and raw API users), only if LWIP_IGMP=1. Add getsockopt processing for
+ IP_MULTICAST_TTL and IP_MULTICAST_IF.
+
+ 2007-09-10 Frédéric Bernon
+ * snmp.h, mib2.c: enable to remove SNMP timer (which consumne several cycles
+ even when it's not necessary). snmp_agent.txt tell to call snmp_inc_sysuptime()
+ each 10ms (but, it's intrusive if you use sys_timeout feature). Now, you can
+ decide to call snmp_add_sysuptime(100) each 1000ms (which is bigger "step", but
+ call to a lower frequency). Or, you can decide to not call snmp_inc_sysuptime()
+ or snmp_add_sysuptime(), and to define the SNMP_GET_SYSUPTIME(sysuptime) macro.
+ This one is undefined by default in mib2.c. SNMP_GET_SYSUPTIME is called inside
+ snmp_get_sysuptime(u32_t *value), and enable to change "sysuptime" value only
+ when it's queried (any direct call to "sysuptime" is changed by a call to
+ snmp_get_sysuptime).
+
+ 2007-09-09 Frédéric Bernon, Bill Florac
+ * igmp.h, igmp.c, netif.h, netif.c, ip.c: To enable to have interfaces with IGMP,
+ and others without it, there is a new NETIF_FLAG_IGMP flag to set in netif->flags
+ if you want IGMP on an interface. igmp_stop() is now called inside netif_remove().
+ igmp_report_groups() is now called inside netif_set_link_up() (need to have
+ LWIP_NETIF_LINK_CALLBACK=1) to resend reports once the link is up (avoid to wait
+ the next query message to receive the matching multicast streams).
+
+ 2007-09-08 Frédéric Bernon
+ * sockets.c, ip.h, api.h, tcp.h: declare a "struct ip_pcb" which only contains
+ IP_PCB. Add in the netconn's "pcb" union a "struct ip_pcb *ip;" (no size change).
+ Use this new field to access to common pcb fields (ttl, tos, so_options, etc...).
+ Enable to access to these fields with LWIP_TCP=0.
+
+ 2007-09-05 Frédéric Bernon
+ * udp.c, ipv4/icmp.c, ipv4/ip.c, ipv6/icmp.c, ipv6/ip6.c, ipv4/icmp.h,
+ ipv6/icmp.h, opt.h: Integrate "task #7272 : LWIP_ICMP option". The new option
+ LWIP_ICMP enable/disable ICMP module inside the IP stack (enable per default).
+ Be careful, disabling ICMP make your product non-compliant to RFC1122, but
+ help to reduce footprint, and to reduce "visibility" on the Internet.
+
+ 2007-09-05 Frédéric Bernon, Bill Florac
+ * opt.h, sys.h, tcpip.c, slipif.c, ppp.c, sys_arch.txt: Change parameters list
+ for sys_thread_new (see "task #7252 : Create sys_thread_new_ex()"). Two new
+ parameters have to be provided: a task name, and a task stack size. For this
+ one, since it's platform dependant, you could define the best one for you in
+ your lwipopts.h. For port maintainers, you can just add these new parameters
+ in your sys_arch.c file, and but it's not mandatory, use them in your OS
+ specific functions.
+
+ 2007-09-05 Frédéric Bernon
+ * inet.c, autoip.c, msg_in.c, msg_out.c, init.c: Move some build time checkings
+ inside init.c for task #7142 "Sanity check user-configurable values".
+
+ 2007-09-04 Frédéric Bernon, Bill Florac
+ * igmp.h, igmp.c, memp_std.h, memp.c, init.c, opt.h: Replace mem_malloc call by
+ memp_malloc, and use a new MEMP_NUM_IGMP_GROUP option (see opt.h to define the
+ value). It will avoid potential fragmentation problems, use a counter to know
+ how many times a group is used on an netif, and free it when all applications
+ leave it. MEMP_NUM_IGMP_GROUP got 8 as default value (and init.c got a sanity
+ check if LWIP_IGMP!=0).
+
+ 2007-09-03 Frédéric Bernon
+ * igmp.h, igmp.c, sockets.c, api_msg.c: Changes for "#20503 IGMP Improvement".
+ Initialize igmp_mac_filter to NULL in netif_add (this field should be set in
+ the netif's "init" function). Use the "imr_interface" field (for socket layer)
+ and/or the "interface" field (for netconn layer), for join/leave operations.
+ The igmp_join/leavegroup first parameter change from a netif to an ipaddr.
+ This field could be a netif's ipaddr, or "any" (same meaning than ip_addr_isany).
+
+ 2007-08-30 Frédéric Bernon
+ * Add netbuf.h, netbuf.c, Change api.h, api_lib.c: #7249 "Split netbuf functions
+ from api/api_lib". Now netbuf API is independant of netconn, and can be used
+ with other API (application based on raw API, or future "socket2" API). Ports
+ maintainers just have to add src/api/netbuf.c in their makefile/projects.
+
+ 2007-08-30 Frédéric Bernon, Jonathan Larmour
+ * init.c: Add first version of lwip_sanity_check for task #7142 "Sanity check
+ user-configurable values".
+
+ 2007-08-29 Frédéric Bernon
+ * igmp.h, igmp.c, tcpip.c, init.c, netif.c: change igmp_init and add igmp_start.
+ igmp_start is call inside netif_add. Now, igmp initialization is in the same
+ spirit than the others modules. Modify some IGMP debug traces.
+
+ 2007-08-29 Frédéric Bernon
+ * Add init.h, init.c, Change opt.h, tcpip.c: Task #7213 "Add a lwip_init function"
+ Add lwip_init function to regroup all modules initializations, and to provide
+ a place to add code for task #7142 "Sanity check user-configurable values".
+ Ports maintainers should remove direct initializations calls from their code,
+ and add init.c in their makefiles. Note that lwip_init() function is called
+ inside tcpip_init, but can also be used by raw api users since all calls are
+ disabled when matching options are disabled. Also note that their is new options
+ in opt.h, you should configure in your lwipopts.h (they are enabled per default).
+
+ 2007-08-26 Marc Boucher
+ * api_msg.c: do_close_internal(): Reset the callbacks and arg (conn) to NULL
+ since they can under certain circumstances be called with an invalid conn
+ pointer after the connection has been closed (and conn has been freed).
+
+ 2007-08-25 Frédéric Bernon (Artem Migaev's Patch)
+ * netif.h, netif.c: Integrate "patch #6163 : Function to check if link layer is up".
+ Add a netif_is_link_up() function if LWIP_NETIF_LINK_CALLBACK option is set.
+
+ 2007-08-22 Frédéric Bernon
+ * netif.h, netif.c, opt.h: Rename LWIP_NETIF_CALLBACK in LWIP_NETIF_STATUS_CALLBACK
+ to be coherent with new LWIP_NETIF_LINK_CALLBACK option before next release.
+
+ 2007-08-22 Frédéric Bernon
+ * tcpip.h, tcpip.c, ethernetif.c, opt.h: remove options ETHARP_TCPIP_INPUT &
+ ETHARP_TCPIP_ETHINPUT, now, only "ethinput" code is supported, even if the
+ name is tcpip_input (we keep the name of 1.2.0 function).
+
+ 2007-08-17 Jared Grubb
+ * memp_std.h, memp.h, memp.c, mem.c, stats.c: (Task #7136) Centralize mempool
+ settings into new memp_std.h and optional user file lwippools.h. This adds
+ more dynamic mempools, and allows the user to create an arbitrary number of
+ mempools for mem_malloc.
+
+ 2007-08-16 Marc Boucher
+ * api_msg.c: Initialize newconn->state to NETCONN_NONE in accept_function;
+ otherwise it was left to NETCONN_CLOSE and sent_tcp() could prematurely
+ close the connection.
+
+ 2007-08-16 Marc Boucher
+ * sockets.c: lwip_accept(): check netconn_peer() error return.
+
+ 2007-08-16 Marc Boucher
+ * mem.c, mem.h: Added mem_calloc().
+
+ 2007-08-16 Marc Boucher
+ * tcpip.c, tcpip.h memp.c, memp.h: Added distinct memp (MEMP_TCPIP_MSG_INPKT)
+ for input packets to prevent floods from consuming all of MEMP_TCPIP_MSG
+ and starving other message types.
+ Renamed MEMP_TCPIP_MSG to MEMP_TCPIP_MSG_API
+
+ 2007-08-16 Marc Boucher
+ * pbuf.c, pbuf.h, etharp.c, tcp_in.c, sockets.c: Split pbuf flags in pbuf
+ type and flgs (later renamed to flags).
+ Use enum pbuf_flag as pbuf_type. Renumber PBUF_FLAG_*.
+ Improved lwip_recvfrom(). TCP push now propagated.
+
+ 2007-08-16 Marc Boucher
+ * ethernetif.c, contrib/ports/various: ethbroadcast now a shared global
+ provided by etharp.
+
+ 2007-08-16 Marc Boucher
+ * ppp_oe.c ppp_oe.h, auth.c chap.c fsm.c lcp.c ppp.c ppp.h,
+ etharp.c ethernetif.c, etharp.h, opt.h tcpip.h, tcpip.c:
+ Added PPPoE support and various PPP improvements.
+
+ 2007-07-25 Simon Goldschmidt
+ * api_lib.c, ip_frag.c, pbuf.c, api.h, pbuf.h: Introduced pbuf_copy_partial,
+ making netbuf_copy_partial use this function.
+
+ 2007-07-25 Simon Goldschmidt
+ * tcp_in.c: Fix bug #20506: Slow start / initial congestion window starts with
+ 2 * mss (instead of 1 * mss previously) to comply with some newer RFCs and
+ other stacks.
+
+ 2007-07-13 Jared Grubb (integrated by Frédéric Bernon)
+ * opt.h, netif.h, netif.c, ethernetif.c: Add new configuration option to add
+ a link callback in the netif struct, and functions to handle it. Be carefull
+ for port maintainers to add the NETIF_FLAG_LINK_UP flag (like in ethernetif.c)
+ if you want to be sure to be compatible with future changes...
+
+ 2007-06-30 Frédéric Bernon
+ * sockets.h, sockets.c: Implement MSG_PEEK flag for recv/recvfrom functions.
+
+ 2007-06-21 Simon Goldschmidt
+ * etharp.h, etharp.c: Combined etharp_request with etharp_raw for both
+ LWIP_AUTOIP =0 and =1 to remove redundant code.
+
+ 2007-06-21 Simon Goldschmidt
+ * mem.c, memp.c, mem.h, memp.h, opt.h: task #6863: Introduced the option
+ MEM_USE_POOLS to use 4 pools with different sized elements instead of a
+ heap. This both prevents memory fragmentation and gives a higher speed
+ at the cost of more memory consumption. Turned off by default.
+
+ 2007-06-21 Simon Goldschmidt
+ * api_lib.c, api_msg.c, api.h, api_msg.h: Converted the length argument of
+ netconn_write (and therefore also api_msg_msg.msg.w.len) from u16_t into
+ int to be able to send a bigger buffer than 64K with one time (mainly
+ used from lwip_send).
+
+ 2007-06-21 Simon Goldschmidt
+ * tcp.h, api_msg.c: Moved the nagle algorithm from netconn_write/do_write
+ into a define (tcp_output_nagle) in tcp.h to provide it to raw api users, too.
+
+ 2007-06-21 Simon Goldschmidt
+ * api.h, api_lib.c, api_msg.c: Fixed bug #20021: Moved sendbuf-processing in
+ netconn_write from api_lib.c to api_msg.c to also prevent multiple context-
+ changes on low memory or empty send-buffer.
+
+ 2007-06-18 Simon Goldschmidt
+ * etharp.c, etharp.h: Changed etharp to use a defined hardware address length
+ of 6 to avoid loading netif->hwaddr_len every time (since this file is only
+ used for ethernet and struct eth_addr already had a defined length of 6).
+
+ 2007-06-17 Simon Goldschmidt
+ * sockets.c, sockets.h: Implemented socket options SO_NO_CHECK for UDP sockets
+ to disable UDP checksum generation on transmit.
+
+ 2007-06-13 Frédéric Bernon, Simon Goldschmidt
+ * debug.h, api_msg.c: change LWIP_ERROR to use it to check errors like invalid
+ pointers or parameters, and let the possibility to redefined it in cc.h. Use
+ this macro to check "conn" parameter in api_msg.c functions.
+
+ 2007-06-11 Simon Goldschmidt
+ * sockets.c, sockets.h: Added UDP lite support for sockets
+
+ 2007-06-10 Simon Goldschmidt
+ * udp.h, opt.h, api_msg.c, ip.c, udp.c: Included switch LWIP_UDPLITE (enabled
+ by default) to switch off UDP-Lite support if not needed (reduces udp.c code
+ size)
+
+ 2007-06-09 Dominik Spies (integrated by Frédéric Bernon)
+ * autoip.h, autoip.c, dhcp.h, dhcp.c, netif.h, netif.c, etharp.h, etharp.c, opt.h:
+ AutoIP implementation available for IPv4, with new options LWIP_AUTOIP and
+ LWIP_DHCP_AUTOIP_COOP if you want to cooperate with DHCP. Some tips to adapt
+ (see TODO mark in the source code).
+
+ 2007-06-09 Simon Goldschmidt
+ * etharp.h, etharp.c, ethernetif.c: Modified order of parameters for
+ etharp_output() to match netif->output so etharp_output() can be used
+ directly as netif->output to save one function call.
+
+ 2007-06-08 Simon Goldschmidt
+ * netif.h, ethernetif.c, slipif.c, loopif.c: Added define
+ NETIF_INIT_SNMP(netif, type, speed) to initialize per-netif snmp variables,
+ added initialization of those to ethernetif, slipif and loopif.
+
+ 2007-05-18 Simon Goldschmidt
+ * opt.h, ip_frag.c, ip_frag.h, ip.c: Added option IP_FRAG_USES_STATIC_BUF
+ (defaulting to off for now) that can be set to 0 to send fragmented
+ packets by passing PBUF_REFs down the stack.
+
+ 2007-05-23 Frédéric Bernon
+ * api_lib.c: Implement SO_RCVTIMEO for accept and recv on TCP
+ connections, such present in patch #5959.
+
+ 2007-05-23 Frédéric Bernon
+ * api.h, api_lib.c, api_msg.c, sockets.c: group the different NETCONN_UDPxxx
+ code in only one part...
+
+ 2007-05-18 Simon Goldschmidt
+ * opt.h, memp.h, memp.c: Added option MEMP_OVERFLOW_CHECK to check for memp
+ elements to overflow. This is achieved by adding some bytes before and after
+ each pool element (increasing their size, of course), filling them with a
+ prominent value and checking them on freeing the element.
+ Set it to 2 to also check every element in every pool each time memp_malloc()
+ or memp_free() is called (slower but more helpful).
+
+ 2007-05-10 Simon Goldschmidt
+ * opt.h, memp.h, memp.c, pbuf.c (see task #6831): use a new memp pool for
+ PBUF_POOL pbufs instead of the old pool implementation in pbuf.c to reduce
+ code size.
+
+ 2007-05-11 Frédéric Bernon
+ * sockets.c, api_lib.c, api_msg.h, api_msg.c, netifapi.h, netifapi.c, tcpip.c:
+ Include a function pointer instead of a table index in the message to reduce
+ footprint. Disable some part of lwip_send and lwip_sendto if some options are
+ not set (LWIP_TCP, LWIP_UDP, LWIP_RAW).
+
+ 2007-05-10 Simon Goldschmidt
+ * *.h (except netif/ppp/*.h): Included patch #5448: include '#ifdef __cplusplus
+ \ extern "C" {' in all header files. Now you can write your application using
+ the lwIP stack in C++ and simply #include the core files. Note I have left
+ out the netif/ppp/*h header files for now, since I don't know which files are
+ included by applications and which are for internal use only.
+
+ 2007-05-09 Simon Goldschmidt
+ * opt.h, *.c/*.h: Included patch #5920: Create define to override C-library
+ memcpy. 2 Defines are created: MEMCPY() for normal memcpy, SMEMCPY() for
+ situations where some compilers might inline the copy and save a function
+ call. Also replaced all calls to memcpy() with calls to (S)MEMCPY().
+
+ 2007-05-08 Simon Goldschmidt
+ * mem.h: If MEM_LIBC_MALLOC==1, allow the defines (e.g. mem_malloc() -> malloc())
+ to be overriden in case the C-library malloc implementation is not protected
+ against concurrent access.
+
+ 2007-05-04 Simon Goldschmidt (Atte Kojo)
+ * etharp.c: Introduced fast one-entry-cache to speed up ARP lookup when sending
+ multiple packets to the same host.
+
+ 2007-05-04 Frédéric Bernon, Jonathan Larmour
+ * sockets.c, api.h, api_lib.c, api_msg.h, api_msg.c: Fix bug #19162 "lwip_sento: a possible
+ to corrupt remote addr/port connection state". Reduce problems "not enought memory" with
+ netbuf (if we receive lot of datagrams). Improve lwip_sendto (only one exchange between
+ sockets api and api_msg which run in tcpip_thread context). Add netconn_sento function.
+ Warning, if you directly access to "fromaddr" & "fromport" field from netbuf struct,
+ these fields are now renamed "addr" & "port".
+
+ 2007-04-11 Jonathan Larmour
+ * sys.h, api_lib.c: Provide new sys_mbox_tryfetch function. Require ports to provide new
+ sys_arch_mbox_tryfetch function to get a message if one is there, otherwise return
+ with SYS_MBOX_EMPTY. sys_arch_mbox_tryfetch can be implemented as a function-like macro
+ by the port in sys_arch.h if desired.
+
+ 2007-04-06 Frédéric Bernon, Simon Goldschmidt
+ * opt.h, tcpip.h, tcpip.c, netifapi.h, netifapi.c: New configuration option LWIP_NETIF_API
+ allow to use thread-safe functions to add/remove netif in list, and to start/stop dhcp
+ clients, using new functions from netifapi.h. Disable as default (no port change to do).
+
+ 2007-04-05 Frédéric Bernon
+ * sockets.c: remplace ENOBUFS errors on alloc_socket by ENFILE to be more BSD compliant.
+
+ 2007-04-04 Simon Goldschmidt
+ * arch.h, api_msg.c, dhcp.c, msg_in.c, sockets.c: Introduced #define LWIP_UNUSED_ARG(x)
+ use this for and architecture-independent form to tell the compiler you intentionally
+ are not using this variable. Can be overriden in cc.h.
+
+ 2007-03-28 Frédéric Bernon
+ * opt.h, netif.h, dhcp.h, dhcp.c: New configuration option LWIP_NETIF_HOSTNAME allow to
+ define a hostname in netif struct (this is just a pointer, so, you can use a hardcoded
+ string, point on one of your's ethernetif field, or alloc a string you will free yourself).
+ It will be used by DHCP to register a client hostname, but can also be use when you call
+ snmp_set_sysname.
+
+ 2007-03-28 Frédéric Bernon
+ * netif.h, netif.c: A new NETIF_FLAG_ETHARP flag is defined in netif.h, to allow to
+ initialize a network interface's flag with. It tell this interface is an ethernet
+ device, and we can use ARP with it to do a "gratuitous ARP" (RFC 3220 "IP Mobility
+ Support for IPv4" section 4.6) when interface is "up" with netif_set_up().
+
+ 2007-03-26 Frédéric Bernon, Jonathan Larmour
+ * opt.h, tcpip.c: New configuration option LWIP_ARP allow to disable ARP init at build
+ time if you only use PPP or SLIP. The default is enable. Note we don't have to call
+ etharp_init in your port's initilization sequence if you use tcpip.c, because this call
+ is done in tcpip_init function.
+
+ 2007-03-22 Frédéric Bernon
+ * stats.h, stats.c, msg_in.c: Stats counters can be change to u32_t if necessary with the
+ new option LWIP_STATS_LARGE. If you need this option, define LWIP_STATS_LARGE to 1 in
+ your lwipopts.h. More, unused counters are not defined in the stats structs, and not
+ display by stats_display(). Note that some options (SYS_STATS and RAW_STATS) are defined
+ but never used. Fix msg_in.c with the correct #if test for a stat display.
+
+ 2007-03-21 Kieran Mansley
+ * netif.c, netif.h: Apply patch#4197 with some changes (originator: rireland@hmgsl.com).
+ Provides callback on netif up/down state change.
+
+ 2007-03-11 Frédéric Bernon, Mace Gael, Steve Reynolds
+ * sockets.h, sockets.c, api.h, api_lib.c, api_msg.h, api_msg.c, igmp.h, igmp.c,
+ ip.c, netif.h, tcpip.c, opt.h:
+ New configuration option LWIP_IGMP to enable IGMP processing. Based on only one
+ filter per all network interfaces. Declare a new function in netif to enable to
+ control the MAC filter (to reduce lwIP traffic processing).
+
+ 2007-03-11 Frédéric Bernon
+ * tcp.h, tcp.c, sockets.c, tcp_out.c, tcp_in.c, opt.h: Keepalive values can
+ be configured at run time with LWIP_TCP_KEEPALIVE, but don't change this
+ unless you know what you're doing (default are RFC1122 compliant). Note
+ that TCP_KEEPIDLE and TCP_KEEPINTVL have to be set in seconds.
+
+ 2007-03-08 Frédéric Bernon
+ * tcp.h: Keepalive values can be configured at compile time, but don't change
+ this unless you know what you're doing (default are RFC1122 compliant).
+
+ 2007-03-08 Frédéric Bernon
+ * sockets.c, api.h, api_lib.c, tcpip.c, sys.h, sys.c, err.c, opt.h:
+ Implement LWIP_SO_RCVTIMEO configuration option to enable/disable SO_RCVTIMEO
+ on UDP sockets/netconn.
+
+ 2007-03-08 Simon Goldschmidt
+ * snmp_msg.h, msg_in.c: SNMP UDP ports can be configured at compile time.
+
+ 2007-03-06 Frédéric Bernon
+ * api.h, api_lib.c, sockets.h, sockets.c, tcpip.c, sys.h, sys.c, err.h:
+ Implement SO_RCVTIMEO on UDP sockets/netconn.
+
+ 2007-02-28 Kieran Mansley (based on patch from Simon Goldschmidt)
+ * api_lib.c, tcpip.c, memp.c, memp.h: make API msg structs allocated
+ on the stack and remove the API msg type from memp
+
+ 2007-02-26 Jonathan Larmour (based on patch from Simon Goldschmidt)
+ * sockets.h, sockets.c: Move socket initialization to new
+ lwip_socket_init() function.
+ NOTE: this changes the API with ports. Ports will have to be
+ updated to call lwip_socket_init() now.
+
+ 2007-02-26 Jonathan Larmour (based on patch from Simon Goldschmidt)
+ * api_lib.c: Use memcpy in netbuf_copy_partial.
+
+
+ ++ Bug fixes:
+
+ 2008-03-17 Frédéric Bernon, Ed Kerekes
+ * igmp.h, igmp.c: Fix bug #22613 "IGMP iphdr problem" (could have
+ some problems to fill the IP header on some targets, use now the
+ ip.h macros to do it).
+
+ 2008-03-13 Frédéric Bernon
+ * sockets.c: Fix bug #22435 "lwip_recvfrom with TCP break;". Using
+ (lwip_)recvfrom with valid "from" and "fromlen" parameters, on a
+ TCP connection caused a crash. Note that using (lwip_)recvfrom
+ like this is a bit slow and that using (lwip)getpeername is the
+ good lwip way to do it (so, using recv is faster on tcp sockets).
+
+ 2008-03-12 Frédéric Bernon, Jonathan Larmour
+ * api_msg.c, contrib/apps/ping.c: Fix bug #22530 "api_msg.c's
+ recv_raw() does not consume data", and the ping sample (with
+ LWIP_SOCKET=1, the code did the wrong supposition that lwip_recvfrom
+ returned the IP payload, without the IP header).
+
+ 2008-03-04 Jonathan Larmour
+ * mem.c, stats.c, mem.h: apply patch #6414 to avoid compiler errors
+ and/or warnings on some systems where mem_size_t and size_t differ.
+ * pbuf.c, ppp.c: Fix warnings on some systems with mem_malloc.
+
+ 2008-03-04 Kieran Mansley (contributions by others)
+ * Numerous small compiler error/warning fixes from contributions to
+ mailing list after 1.3.0 release candidate made.
+
+ 2008-01-25 Cui hengbin (integrated by Frédéric Bernon)
+ * dns.c: Fix bug #22108 "DNS problem" caused by unaligned structures.
+
+ 2008-01-15 Kieran Mansley
+ * tcp_out.c: BUG20511. Modify persist timer to start when we are
+ prevented from sending by a small send window, not just a zero
+ send window.
+
+ 2008-01-09 Jonathan Larmour
+ * opt.h, ip.c: Rename IP_OPTIONS define to IP_OPTIONS_ALLOWED to avoid
+ conflict with Linux system headers.
+
+ 2008-01-06 Jonathan Larmour
+ * dhcp.c: fix bug #19927: "DHCP NACK problem" by clearing any existing set IP
+ address entirely on receiving a DHCPNAK, and restarting discovery.
+
+ 2007-12-21 Simon Goldschmidt
+ * sys.h, api_lib.c, api_msg.c, sockets.c: fix bug #21698: "netconn->recv_avail
+ is not protected" by using new macros for interlocked access to modify/test
+ netconn->recv_avail.
+
+ 2007-12-20 Kieran Mansley (based on patch from Oleg Tyshev)
+ * tcp_in.c: fix bug# 21535 (nrtx not reset correctly in SYN_SENT state)
+
+ 2007-12-20 Kieran Mansley (based on patch from Per-Henrik Lundbolm)
+ * tcp.c, tcp_in.c, tcp_out.c, tcp.h: fix bug #20199 (better handling
+ of silly window avoidance and prevent lwIP from shrinking the window)
+
+ 2007-12-04 Simon Goldschmidt
+ * tcp.c, tcp_in.c: fix bug #21699 (segment leak in ooseq processing when last
+ data packet was lost): add assert that all segment lists are empty in
+ tcp_pcb_remove before setting pcb to CLOSED state; don't directly set CLOSED
+ state from LAST_ACK in tcp_process
+
+ 2007-12-02 Simon Goldschmidt
+ * sockets.h: fix bug #21654: exclude definition of struct timeval from #ifndef FD_SET
+ If including <sys/time.h> for system-struct timeval, LWIP_TIMEVAL_PRIVATE now
+ has to be set to 0 in lwipopts.h
+
+ 2007-12-02 Simon Goldschmidt
+ * api_msg.c, api_lib.c: fix bug #21656 (recvmbox problem in netconn API): always
+ allocate a recvmbox in netconn_new_with_proto_and_callback. For a tcp-listen
+ netconn, this recvmbox is later freed and a new mbox is allocated for acceptmbox.
+ This is a fix for thread-safety and allocates all items needed for a netconn
+ when the netconn is created.
+
+ 2007-11-30 Simon Goldschmidt
+ * udp.c: first attempt to fix bug #21655 (DHCP doesn't work reliably with multiple
+ netifs): if LWIP_DHCP is enabled, UDP packets to DHCP_CLIENT_PORT are passed
+ to netif->dhcp->pcb only (if that exists) and not to any other pcb for the same
+ port (only solution to let UDP pcbs 'bind' to a netif instead of an IP address)
+
+ 2007-11-27 Simon Goldschmidt
+ * ip.c: fixed bug #21643 (udp_send/raw_send don't fail if netif is down) by
+ letting ip_route only use netifs that are up.
+
+ 2007-11-27 Simon Goldschmidt
+ * err.h, api_lib.c, api_msg.c, sockets.c: Changed error handling: ERR_MEM, ERR_BUF
+ and ERR_RTE are seen as non-fatal, all other errors are fatal. netconns and
+ sockets block most operations once they have seen a fatal error.
+
+ 2007-11-27 Simon Goldschmidt
+ * udp.h, udp.c, dhcp.c: Implemented new function udp_sendto_if which takes the
+ netif to send as an argument (to be able to send on netifs that are down).
+
+ 2007-11-26 Simon Goldschmidt
+ * tcp_in.c: Fixed bug #21582: pcb->acked accounting can be wrong when ACKs
+ arrive out-of-order
+
+ 2007-11-21 Simon Goldschmidt
+ * tcp.h, tcp_out.c, api_msg.c: Fixed bug #20287: tcp_output_nagle sends too early
+ Fixed the nagle algorithm; nagle now also works for all raw API applications
+ and has to be explicitly disabled with 'tcp_pcb->flags |= TF_NODELAY'
+
+ 2007-11-12 Frédéric Bernon
+ * sockets.c, api.h, api_lib.c, api_msg.h, api_msg.c: Fixed bug #20900. Now, most
+ of the netconn_peer and netconn_addr processing is done inside tcpip_thread
+ context in do_getaddr.
+
+ 2007-11-10 Simon Goldschmidt
+ * etharp.c: Fixed bug: assert fired when MEMP_ARP_QUEUE was empty (which can
+ happen any time). Now the packet simply isn't enqueued when out of memory.
+
+ 2007-11-01 Simon Goldschmidt
+ * tcp.c, tcp_in.c: Fixed bug #21494: The send mss (pcb->mss) is set to 536 (or
+ TCP_MSS if that is smaller) as long as no MSS option is received from the
+ remote host.
+
+ 2007-11-01 Simon Goldschmidt
+ * tcp.h, tcp.c, tcp_in.c: Fixed bug #21491: The MSS option sent (with SYN)
+ is now based on TCP_MSS instead of pcb->mss (on passive open now effectively
+ sending our configured TCP_MSS instead of the one received).
+
+ 2007-11-01 Simon Goldschmidt
+ * tcp_in.c: Fixed bug #21181: On active open, the initial congestion window was
+ calculated based on the configured TCP_MSS, not on the MSS option received
+ with SYN+ACK.
+
+ 2007-10-09 Simon Goldschmidt
+ * udp.c, inet.c, inet.h: Fixed UDPLite: send: Checksum was always generated too
+ short and also was generated wrong if checksum coverage != tot_len;
+ receive: checksum was calculated wrong if checksum coverage != tot_len
+
+ 2007-10-08 Simon Goldschmidt
+ * mem.c: lfree was not updated in mem_realloc!
+
+ 2007-10-07 Frédéric Bernon
+ * sockets.c, api.h, api_lib.c: First step to fix "bug #20900 : Potential
+ crash error problem with netconn_peer & netconn_addr". VERY IMPORTANT:
+ this change cause an API breakage for netconn_addr, since a parameter
+ type change. Any compiler should cause an error without any changes in
+ yours netconn_peer calls (so, it can't be a "silent change"). It also
+ reduce a little bit the footprint for socket layer (lwip_getpeername &
+ lwip_getsockname use now a common lwip_getaddrname function since
+ netconn_peer & netconn_addr have the same parameters).
+
+ 2007-09-20 Simon Goldschmidt
+ * tcp.c: Fixed bug #21080 (tcp_bind without check pcbs in TIME_WAIT state)
+ by checking tcp_tw_pcbs also
+
+ 2007-09-19 Simon Goldschmidt
+ * icmp.c: Fixed bug #21107 (didn't reset IP TTL in ICMP echo replies)
+
+ 2007-09-15 Mike Kleshov
+ * mem.c: Fixed bug #21077 (inaccuracy in calculation of lwip_stat.mem.used)
+
+ 2007-09-06 Frédéric Bernon
+ * several-files: replace some #include "arch/cc.h" by "lwip/arch.h", or simply remove
+ it as long as "lwip/opt.h" is included before (this one include "lwip/debug.h" which
+ already include "lwip/arch.h"). Like that, default defines are provided by "lwip/arch.h"
+ if they are not defined in cc.h, in the same spirit than "lwip/opt.h" for lwipopts.h.
+
+ 2007-08-30 Frédéric Bernon
+ * igmp.h, igmp.c: Some changes to remove some redundant code, add some traces,
+ and fix some coding style.
+
+ 2007-08-28 Frédéric Bernon
+ * tcpip.c: Fix TCPIP_MSG_INPKT processing: now, tcpip_input can be used for any
+ kind of packets. These packets are considered like Ethernet packets (payload
+ pointing to ethhdr) if the netif got the NETIF_FLAG_ETHARP flag. Else, packets
+ are considered like IP packets (payload pointing to iphdr).
+
+ 2007-08-27 Frédéric Bernon
+ * api.h, api_lib.c, api_msg.c: First fix for "bug #20900 : Potential crash error
+ problem with netconn_peer & netconn_addr". Introduce NETCONN_LISTEN netconn_state
+ and remove obsolete ones (NETCONN_RECV & NETCONN_ACCEPT).
+
+ 2007-08-24 Kieran Mansley
+ * inet.c Modify (acc >> 16) test to ((acc >> 16) != 0) to help buggy
+ compiler (Paradigm C++)
+
+ 2007-08-09 Frédéric Bernon, Bill Florac
+ * stats.h, stats.c, igmp.h, igmp.c, opt.h: Fix for bug #20503 : IGMP Improvement.
+ Introduce IGMP_STATS to centralize statistics management.
+
+ 2007-08-09 Frédéric Bernon, Bill Florac
+ * udp.c: Fix for bug #20503 : IGMP Improvement. Enable to receive a multicast
+ packet on a udp pcb binded on an netif's IP address, and not on "any".
+
+ 2007-08-09 Frédéric Bernon, Bill Florac
+ * igmp.h, igmp.c, ip.c: Fix minor changes from bug #20503 : IGMP Improvement.
+ This is mainly on using lookup/lookfor, and some coding styles...
+
+ 2007-07-26 Frédéric Bernon (and "thedoctor")
+ * igmp.c: Fix bug #20595 to accept IGMPv3 "Query" messages.
+
+ 2007-07-25 Simon Goldschmidt
+ * api_msg.c, tcp.c: Another fix for bug #20021: by not returning an error if
+ tcp_output fails in tcp_close, the code in do_close_internal gets simpler
+ (tcp_output is called again later from tcp timers).
+
+ 2007-07-25 Simon Goldschmidt
+ * ip_frag.c: Fixed bug #20429: use the new pbuf_copy_partial instead of the old
+ copy_from_pbuf, which illegally modified the given pbuf.
+
+ 2007-07-25 Simon Goldschmidt
+ * tcp_out.c: tcp_enqueue: pcb->snd_queuelen didn't work for chaine PBUF_RAMs:
+ changed snd_queuelen++ to snd_queuelen += pbuf_clen(p).
+
+ 2007-07-24 Simon Goldschmidt
+ * api_msg.c, tcp.c: Fix bug #20480: Check the pcb passed to tcp_listen() for the
+ correct state (must be CLOSED).
+
+ 2007-07-13 Thomas Taranowski (commited by Jared Grubb)
+ * memp.c: Fix bug #20478: memp_malloc returned NULL+MEMP_SIZE on failed
+ allocation. It now returns NULL.
+
+ 2007-07-13 Frédéric Bernon
+ * api_msg.c: Fix bug #20318: api_msg "recv" callbacks don't call pbuf_free in
+ all error cases.
+
+ 2007-07-13 Frédéric Bernon
+ * api_msg.c: Fix bug #20315: possible memory leak problem if tcp_listen failed,
+ because current code doesn't follow rawapi.txt documentation.
+
+ 2007-07-13 Kieran Mansley
+ * src/core/tcp_in.c Apply patch#5741 from Oleg Tyshev to fix bug in
+ out of sequence processing of received packets
+
+ 2007-07-03 Simon Goldschmidt
+ * nearly-all-files: Added assertions where PBUF_RAM pbufs are used and an
+ assumption is made that this pbuf is in one piece (i.e. not chained). These
+ assumptions clash with the possibility of converting to fully pool-based
+ pbuf implementations, where PBUF_RAM pbufs might be chained.
+
+ 2007-07-03 Simon Goldschmidt
+ * api.h, api_lib.c, api_msg.c: Final fix for bug #20021 and some other problems
+ when closing tcp netconns: removed conn->sem, less context switches when
+ closing, both netconn_close and netconn_delete should safely close tcp
+ connections.
+
+ 2007-07-02 Simon Goldschmidt
+ * ipv4/ip.h, ipv6/ip.h, opt.h, netif.h, etharp.h, ipv4/ip.c, netif.c, raw.c,
+ tcp_out.c, udp.c, etharp.c: Added option LWIP_NETIF_HWADDRHINT (default=off)
+ to cache ARP table indices with each pcb instead of single-entry cache for
+ the complete stack.
+
+ 2007-07-02 Simon Goldschmidt
+ * tcp.h, tcp.c, tcp_in.c, tcp_out.c: Added some ASSERTS and casts to prevent
+ warnings when assigning to smaller types.
+
+ 2007-06-28 Simon Goldschmidt
+ * tcp_out.c: Added check to prevent tcp_pcb->snd_queuelen from overflowing.
+
+ 2007-06-28 Simon Goldschmidt
+ * tcp.h: Fixed bug #20287: Fixed nagle algorithm (sending was done too early if
+ a segment contained chained pbufs)
+
+ 2007-06-28 Frédéric Bernon
+ * autoip.c: replace most of rand() calls by a macro LWIP_AUTOIP_RAND which compute
+ a "pseudo-random" value based on netif's MAC and some autoip fields. It's always
+ possible to define this macro in your own lwipopts.h to always use C library's
+ rand(). Note that autoip_create_rand_addr doesn't use this macro.
+
+ 2007-06-28 Frédéric Bernon
+ * netifapi.h, netifapi.c, tcpip.h, tcpip.c: Update code to handle the option
+ LWIP_TCPIP_CORE_LOCKING, and do some changes to be coherent with last modifications
+ in api_lib/api_msg (use pointers and not type with table, etc...)
+
+ 2007-06-26 Simon Goldschmidt
+ * udp.h: Fixed bug #20259: struct udp_hdr was lacking the packin defines.
+
+ 2007-06-25 Simon Goldschmidt
+ * udp.c: Fixed bug #20253: icmp_dest_unreach was called with a wrong p->payload
+ for udp packets with no matching pcb.
+
+ 2007-06-25 Simon Goldschmidt
+ * udp.c: Fixed bug #20220: UDP PCB search in udp_input(): a non-local match
+ could get udp input packets if the remote side matched.
+
+ 2007-06-13 Simon Goldschmidt
+ * netif.c: Fixed bug #20180 (TCP pcbs listening on IP_ADDR_ANY could get
+ changed in netif_set_ipaddr if previous netif->ip_addr.addr was 0.
+
+ 2007-06-13 Simon Goldschmidt
+ * api_msg.c: pcb_new sets conn->err if protocol is not implemented
+ -> netconn_new_..() does not allocate a new connection for unsupported
+ protocols.
+
+ 2007-06-13 Frédéric Bernon, Simon Goldschmidt
+ * api_lib.c: change return expression in netconn_addr and netconn_peer, because
+ conn->err was reset to ERR_OK without any reasons (and error was lost)...
+
+ 2007-06-13 Frédéric Bernon, Matthias Weisser
+ * opt.h, mem.h, mem.c, memp.c, pbuf.c, ip_frag.c, vj.c: Fix bug #20162. Rename
+ MEM_ALIGN in LWIP_MEM_ALIGN and MEM_ALIGN_SIZE in LWIP_MEM_ALIGN_SIZE to avoid
+ some macro names collision with some OS macros.
+
+ 2007-06-11 Simon Goldschmidt
+ * udp.c: UDP Lite: corrected the use of chksum_len (based on RFC3828: if it's 0,
+ create checksum over the complete packet. On RX, if it's < 8 (and not 0),
+ discard the packet. Also removed the duplicate 'udphdr->chksum = 0' for both
+ UDP & UDP Lite.
+
+ 2007-06-11 Srinivas Gollakota & Oleg Tyshev
+ * tcp_out.c: Fix for bug #20075 : "A problem with keep-alive timer and TCP flags"
+ where TCP flags wasn't initialized in tcp_keepalive.
+
+ 2007-06-03 Simon Goldschmidt
+ * udp.c: udp_input(): Input pbuf was not freed if pcb had no recv function
+ registered, p->payload was modified without modifying p->len if sending
+ icmp_dest_unreach() (had no negative effect but was definitively wrong).
+
+ 2007-06-03 Simon Goldschmidt
+ * icmp.c: Corrected bug #19937: For responding to an icmp echo request, icmp
+ re-used the input pbuf even if that didn't have enough space to include the
+ link headers. Now the space is tested and a new pbuf is allocated for the
+ echo response packet if the echo request pbuf isn't big enough.
+
+ 2007-06-01 Simon Goldschmidt
+ * sockets.c: Checked in patch #5914: Moved sockopt processing into tcpip_thread.
+
+ 2007-05-23 Frédéric Bernon
+ * api_lib.c, sockets.c: Fixed bug #5958 for netconn_listen (acceptmbox only
+ allocated by do_listen if success) and netconn_accept errors handling. In
+ most of api_lib functions, we replace some errors checkings like "if (conn==NULL)"
+ by ASSERT, except for netconn_delete.
+
+ 2007-05-23 Frédéric Bernon
+ * api_lib.c: Fixed bug #5957 "Safe-thread problem inside netconn_recv" to return
+ an error code if it's impossible to fetch a pbuf on a TCP connection (and not
+ directly close the recvmbox).
+
+ 2007-05-22 Simon Goldschmidt
+ * tcp.c: Fixed bug #1895 (tcp_bind not correct) by introducing a list of
+ bound but unconnected (and non-listening) tcp_pcbs.
+
+ 2007-05-22 Frédéric Bernon
+ * sys.h, sys.c, api_lib.c, tcpip.c: remove sys_mbox_fetch_timeout() (was only
+ used for LWIP_SO_RCVTIMEO option) and use sys_arch_mbox_fetch() instead of
+ sys_mbox_fetch() in api files. Now, users SHOULD NOT use internal lwIP features
+ like "sys_timeout" in their application threads.
+
+ 2007-05-22 Frédéric Bernon
+ * api.h, api_lib.c, api_msg.h, api_msg.c: change the struct api_msg_msg to see
+ which parameters are used by which do_xxx function, and to avoid "misusing"
+ parameters (patch #5938).
+
+ 2007-05-22 Simon Goldschmidt
+ * api_lib.c, api_msg.c, raw.c, api.h, api_msg.h, raw.h: Included patch #5938:
+ changed raw_pcb.protocol from u16_t to u8_t since for IPv4 and IPv6, proto
+ is only 8 bits wide. This affects the api, as there, the protocol was
+ u16_t, too.
+
+ 2007-05-18 Simon Goldschmidt
+ * memp.c: addition to patch #5913: smaller pointer was returned but
+ memp_memory was the same size -> did not save memory.
+
+ 2007-05-16 Simon Goldschmidt
+ * loopif.c, slipif.c: Fix bug #19729: free pbuf if netif->input() returns
+ != ERR_OK.
+
+ 2007-05-16 Simon Goldschmidt
+ * api_msg.c, udp.c: If a udp_pcb has a local_ip set, check if it is the same
+ as the one of the netif used for sending to prevent sending from old
+ addresses after a netif address gets changed (partly fixes bug #3168).
+
+ 2007-05-16 Frédéric Bernon
+ * tcpip.c, igmp.h, igmp.c: Fixed bug "#19800 : IGMP: igmp_tick() will not work
+ with NO_SYS=1". Note that igmp_init is always in tcpip_thread (and not in
+ tcpip_init) because we have to be sure that network interfaces are already
+ added (mac filter is updated only in igmp_init for the moment).
+
+ 2007-05-16 Simon Goldschmidt
+ * mem.c, memp.c: Removed semaphores from memp, changed sys_sem_wait calls
+ into sys_arch_sem_wait calls to prevent timers from running while waiting
+ for the heap. This fixes bug #19167.
+
+ 2007-05-13 Simon Goldschmidt
+ * tcp.h, sockets.h, sockets.c: Fixed bug from patch #5865 by moving the defines
+ for socket options (lwip_set/-getsockopt) used with level IPPROTO_TCP from
+ tcp.h to sockets.h.
+
+ 2007-05-07 Simon Goldschmidt
+ * mem.c: Another attempt to fix bug #17922.
+
+ 2007-05-04 Simon Goldschmidt
+ * pbuf.c, pbuf.h, etharp.c: Further update to ARP queueing: Changed pbuf_copy()
+ implementation so that it can be reused (don't allocate the target
+ pbuf inside pbuf_copy()).
+
+ 2007-05-04 Simon Goldschmidt
+ * memp.c: checked in patch #5913: in memp_malloc() we can return memp as mem
+ to save a little RAM (next pointer of memp is not used while not in pool).
+
+ 2007-05-03 "maq"
+ * sockets.c: Fix ioctl FIONREAD when some data remains from last recv.
+ (patch #3574).
+
+ 2007-04-23 Simon Goldschmidt
+ * loopif.c, loopif.h, opt.h, src/netif/FILES: fix bug #2595: "loopif results
+ in NULL reference for incoming TCP packets". Loopif has to be configured
+ (using LWIP_LOOPIF_MULTITHREADING) to directly call netif->input()
+ (multithreading environments, e.g. netif->input() = tcpip_input()) or
+ putting packets on a list that is fed to the stack by calling loopif_poll()
+ (single-thread / NO_SYS / polling environment where e.g.
+ netif->input() = ip_input).
+
+ 2007-04-17 Jonathan Larmour
+ * pbuf.c: Use s32_t in pbuf_realloc(), as an s16_t can't reliably hold
+ the difference between two u16_t's.
+ * sockets.h: FD_SETSIZE needs to match number of sockets, which is
+ MEMP_NUM_NETCONN in sockets.c right now.
+
+ 2007-04-12 Jonathan Larmour
+ * icmp.c: Reset IP header TTL in ICMP ECHO responses (bug #19580).
+
+ 2007-04-12 Kieran Mansley
+ * tcp.c, tcp_in.c, tcp_out.c, tcp.h: Modify way the retransmission
+ timer is reset to fix bug#19434, with help from Oleg Tyshev.
+
+ 2007-04-11 Simon Goldschmidt
+ * etharp.c, pbuf.c, pbuf.h: 3rd fix for bug #11400 (arp-queuing): More pbufs than
+ previously thought need to be copied (everything but PBUF_ROM!). Cleaned up
+ pbuf.c: removed functions no needed any more (by etharp).
+
+ 2007-04-11 Kieran Mansley
+ * inet.c, ip_addr.h, sockets.h, sys.h, tcp.h: Apply patch #5745: Fix
+ "Constant is long" warnings with 16bit compilers. Contributed by
+ avatar@mmlab.cse.yzu.edu.tw
+
+ 2007-04-05 Frédéric Bernon, Jonathan Larmour
+ * api_msg.c: Fix bug #16830: "err_tcp() posts to connection mailbox when no pend on
+ the mailbox is active". Now, the post is only done during a connect, and do_send,
+ do_write and do_join_leave_group don't do anything if a previous error was signaled.
+
+ 2007-04-03 Frédéric Bernon
+ * ip.c: Don't set the IP_DF ("Don't fragment") flag in the IP header in IP output
+ packets. See patch #5834.
+
+ 2007-03-30 Frédéric Bernon
+ * api_msg.c: add a "pcb_new" helper function to avoid redundant code, and to add
+ missing pcb allocations checking (in do_bind, and for each raw_new). Fix style.
+
+ 2007-03-30 Frédéric Bernon
+ * most of files: prefix all debug.h define with "LWIP_" to avoid any conflict with
+ others environment defines (these were too "generic").
+
+ 2007-03-28 Frédéric Bernon
+ * api.h, api_lib.c, sockets.c: netbuf_ref doesn't check its internal pbuf_alloc call
+ result and can cause a crash. lwip_send now check netbuf_ref result.
+
+ 2007-03-28 Simon Goldschmidt
+ * sockets.c Remove "#include <errno.h>" from sockets.c to avoid multiple
+ definition of macros (in errno.h and lwip/arch.h) if LWIP_PROVIDE_ERRNO is
+ defined. This is the way it should have been already (looking at
+ doc/sys_arch.txt)
+
+ 2007-03-28 Kieran Mansley
+ * opt.h Change default PBUF_POOL_BUFSIZE (again) to accomodate default MSS +
+ IP and TCP headers *and* physical link headers
+
+ 2007-03-26 Frédéric Bernon (based on patch from Dmitry Potapov)
+ * api_lib.c: patch for netconn_write(), fixes a possible race condition which cause
+ to send some garbage. It is not a definitive solution, but the patch does solve
+ the problem for most cases.
+
+ 2007-03-22 Frédéric Bernon
+ * api_msg.h, api_msg.c: Remove obsolete API_MSG_ACCEPT and do_accept (never used).
+
+ 2007-03-22 Frédéric Bernon
+ * api_lib.c: somes resources couldn't be freed if there was errors during
+ netconn_new_with_proto_and_callback.
+
+ 2007-03-22 Frédéric Bernon
+ * ethernetif.c: update netif->input calls to check return value. In older ports,
+ it's a good idea to upgrade them, even if before, there could be another problem
+ (access to an uninitialized mailbox).
+
+ 2007-03-21 Simon Goldschmidt
+ * sockets.c: fixed bug #5067 (essentialy a signed/unsigned warning fixed
+ by casting to unsigned).
+
+ 2007-03-21 Frédéric Bernon
+ * api_lib.c, api_msg.c, tcpip.c: integrate sys_mbox_fetch(conn->mbox, NULL) calls from
+ api_lib.c to tcpip.c's tcpip_apimsg(). Now, use a local variable and not a
+ dynamic one from memp to send tcpip_msg to tcpip_thread in a synchrone call.
+ Free tcpip_msg from tcpip_apimsg is not done in tcpip_thread. This give a
+ faster and more reliable communication between api_lib and tcpip.
+
+ 2007-03-21 Frédéric Bernon
+ * opt.h: Add LWIP_NETIF_CALLBACK (to avoid compiler warning) and set it to 0.
+
+ 2007-03-21 Frédéric Bernon
+ * api_msg.c, igmp.c, igmp.h: Fix C++ style comments
+
+ 2007-03-21 Kieran Mansley
+ * opt.h Change default PBUF_POOL_BUFSIZE to accomodate default MSS +
+ IP and TCP headers
+
+ 2007-03-21 Kieran Mansley
+ * Fix all uses of pbuf_header to check the return value. In some
+ cases just assert if it fails as I'm not sure how to fix them, but
+ this is no worse than before when they would carry on regardless
+ of the failure.
+
+ 2007-03-21 Kieran Mansley
+ * sockets.c, igmp.c, igmp.h, memp.h: Fix C++ style comments and
+ comment out missing header include in icmp.c
+
+ 2007-03-20 Frédéric Bernon
+ * memp.h, stats.c: Fix stats_display function where memp_names table wasn't
+ synchronized with memp.h.
+
+ 2007-03-20 Frédéric Bernon
+ * tcpip.c: Initialize tcpip's mbox, and verify if initialized in tcpip_input,
+ tcpip_ethinput, tcpip_callback, tcpip_apimsg, to fix a init problem with
+ network interfaces. Also fix a compiler warning.
+
+ 2007-03-20 Kieran Mansley
+ * udp.c: Only try and use pbuf_header() to make space for headers if
+ not a ROM or REF pbuf.
+
+ 2007-03-19 Frédéric Bernon
+ * api_msg.h, api_msg.c, tcpip.h, tcpip.c: Add return types to tcpip_apimsg()
+ and api_msg_post().
+
+ 2007-03-19 Frédéric Bernon
+ * Remove unimplemented "memp_realloc" function from memp.h.
+
+ 2007-03-11 Simon Goldschmidt
+ * pbuf.c: checked in patch #5796: pbuf_alloc: len field claculation caused
+ memory corruption.
+
+ 2007-03-11 Simon Goldschmidt (based on patch from Dmitry Potapov)
+ * api_lib.c, sockets.c, api.h, api_msg.h, sockets.h: Fixed bug #19251
+ (missing `const' qualifier in socket functions), to get more compatible to
+ standard POSIX sockets.
+
+ 2007-03-11 Frédéric Bernon (based on patch from Dmitry Potapov)
+ * sockets.c: Add asserts inside bind, connect and sendto to check input
+ parameters. Remove excessive set_errno() calls after get_socket(), because
+ errno is set inside of get_socket(). Move last sock_set_errno() inside
+ lwip_close.
+
+ 2007-03-09 Simon Goldschmidt
+ * memp.c: Fixed bug #11400: New etharp queueing introduced bug: memp_memory
+ was allocated too small.
+
+ 2007-03-06 Simon Goldschmidt
+ * tcpip.c: Initialize dhcp timers in tcpip_thread (if LWIP_DHCP) to protect
+ the stack from concurrent access.
+
+ 2007-03-06 Frédéric Bernon, Dmitry Potapov
+ * tcpip.c, ip_frag.c, ethernetif.c: Fix some build problems, and a redundancy
+ call to "lwip_stats.link.recv++;" in low_level_input() & ethernetif_input().
+
+ 2007-03-06 Simon Goldschmidt
+ * ip_frag.c, ip_frag.h: Reduce code size: don't include code in those files
+ if IP_FRAG == 0 and IP_REASSEMBLY == 0
+
+ 2007-03-06 Frédéric Bernon, Simon Goldschmidt
+ * opt.h, ip_frag.h, tcpip.h, tcpip.c, ethernetif.c: add new configuration
+ option named ETHARP_TCPIP_ETHINPUT, which enable the new tcpip_ethinput.
+ Allow to do ARP processing for incoming packets inside tcpip_thread
+ (protecting ARP layer against concurrent access). You can also disable
+ old code using tcp_input with new define ETHARP_TCPIP_INPUT set to 0.
+ Older ports have to use tcpip_ethinput.
+
+ 2007-03-06 Simon Goldschmidt (based on patch from Dmitry Potapov)
+ * err.h, err.c: fixed compiler warning "initialization dircards qualifiers
+ from pointer target type"
+
+ 2007-03-05 Frédéric Bernon
+ * opt.h, sockets.h: add new configuration options (LWIP_POSIX_SOCKETS_IO_NAMES,
+ ETHARP_TRUST_IP_MAC, review SO_REUSE)
+
+ 2007-03-04 Frédéric Bernon
+ * api_msg.c: Remove some compiler warnings : parameter "pcb" was never
+ referenced.
+
+ 2007-03-04 Frédéric Bernon
+ * api_lib.c: Fix "[patch #5764] api_lib.c cleanup: after patch #5687" (from
+ Dmitry Potapov).
+ The api_msg struct stay on the stack (not moved to netconn struct).
+
+ 2007-03-04 Simon Goldschmidt (based on patch from Dmitry Potapov)
+ * pbuf.c: Fix BUG#19168 - pbuf_free can cause deadlock (if
+ SYS_LIGHTWEIGHT_PROT=1 & freeing PBUF_RAM when mem_sem is not available)
+ Also fixed cast warning in pbuf_alloc()
+
+ 2007-03-04 Simon Goldschmidt
+ * etharp.c, etharp.h, memp.c, memp.h, opt.h: Fix BUG#11400 - don't corrupt
+ existing pbuf chain when enqueuing multiple pbufs to a pending ARP request
+
+ 2007-03-03 Frédéric Bernon
+ * udp.c: remove obsolete line "static struct udp_pcb *pcb_cache = NULL;"
+ It is static, and never used in udp.c except udp_init().
+
+ 2007-03-02 Simon Goldschmidt
+ * tcpip.c: Moved call to ip_init(), udp_init() and tcp_init() from
+ tcpip_thread() to tcpip_init(). This way, raw API connections can be
+ initialized before tcpip_thread is running (e.g. before OS is started)
+
+ 2007-03-02 Frédéric Bernon
+ * rawapi.txt: Fix documentation mismatch with etharp.h about etharp_tmr's call
+ interval.
+
+ 2007-02-28 Kieran Mansley
+ * pbuf.c: Fix BUG#17645 - ensure pbuf payload pointer is not moved
+ outside the region of the pbuf by pbuf_header()
+
+ 2007-02-28 Kieran Mansley
+ * sockets.c: Fix BUG#19161 - ensure milliseconds timeout is non-zero
+ when supplied timeout is also non-zero
+
+(STABLE-1.2.0)
+
+ 2006-12-05 Leon Woestenberg
+ * CHANGELOG: Mention STABLE-1.2.0 release.
+
+ ++ New features:
+
+ 2006-12-01 Christiaan Simons
+ * mem.h, opt.h: Added MEM_LIBC_MALLOC option.
+ Note this is a workaround. Currently I have no other options left.
+
+ 2006-10-26 Christiaan Simons (accepted patch by Jonathan Larmour)
+ * ipv4/ip_frag.c: rename MAX_MTU to IP_FRAG_MAX_MTU and move define
+ to include/lwip/opt.h.
+ * ipv4/lwip/ip_frag.h: Remove unused IP_REASS_INTERVAL.
+ Move IP_REASS_MAXAGE and IP_REASS_BUFSIZE to include/lwip/opt.h.
+ * opt.h: Add above new options.
+
+ 2006-08-18 Christiaan Simons
+ * tcp_{in,out}.c: added SNMP counters.
+ * ipv4/ip.c: added SNMP counters.
+ * ipv4/ip_frag.c: added SNMP counters.
+
+ 2006-08-08 Christiaan Simons
+ * etharp.{c,h}: added etharp_find_addr() to read
+ (stable) ethernet/IP address pair from ARP table
+
+ 2006-07-14 Christiaan Simons
+ * mib_structs.c: added
+ * include/lwip/snmp_structs.h: added
+ * netif.{c,h}, netif/ethernetif.c: added SNMP statistics to netif struct
+
+ 2006-07-06 Christiaan Simons
+ * snmp/asn1_{enc,dec}.c added
+ * snmp/mib2.c added
+ * snmp/msg_{in,out}.c added
+ * include/lwip/snmp_asn1.h added
+ * include/lwip/snmp_msg.h added
+ * doc/snmp_agent.txt added
+
+ 2006-03-29 Christiaan Simons
+ * inet.c, inet.h: Added platform byteswap support.
+ Added LWIP_PLATFORM_BYTESWAP define (defaults to 0) and
+ optional LWIP_PLATFORM_HTONS(), LWIP_PLATFORM_HTONL() macros.
+
+ ++ Bug fixes:
+
+ 2006-11-30 Christiaan Simons
+ * dhcp.c: Fixed false triggers of request_timeout.
+
+ 2006-11-28 Christiaan Simons
+ * netif.c: In netif_add() fixed missing clear of ip_addr, netmask, gw and flags.
+
+ 2006-10-11 Christiaan Simons
+ * api_lib.c etharp.c, ip.c, memp.c, stats.c, sys.{c,h} tcp.h:
+ Partially accepted patch #5449 for ANSI C compatibility / build fixes.
+ * ipv4/lwip/ip.h ipv6/lwip/ip.h: Corrected UDP-Lite protocol
+ identifier from 170 to 136 (bug #17574).
+
+ 2006-10-10 Christiaan Simons
+ * api_msg.c: Fixed Nagle algorithm as reported by Bob Grice.
+
+ 2006-08-17 Christiaan Simons
+ * udp.c: Fixed bug #17200, added check for broadcast
+ destinations for PCBs bound to a unicast address.
+
+ 2006-08-07 Christiaan Simons
+ * api_msg.c: Flushing TCP output in do_close() (bug #15926).
+
+ 2006-06-27 Christiaan Simons
+ * api_msg.c: Applied patch for cold case (bug #11135).
+ In accept_function() ensure newconn->callback is always initialized.
+
+ 2006-06-15 Christiaan Simons
+ * mem.h: added MEM_SIZE_F alias to fix an ancient cold case (bug #1748),
+ facilitate printing of mem_size_t and u16_t statistics.
+
+ 2006-06-14 Christiaan Simons
+ * api_msg.c: Applied patch #5146 to handle allocation failures
+ in accept() by Kevin Lawson.
+
+ 2006-05-26 Christiaan Simons
+ * api_lib.c: Removed conn->sem creation and destruction
+ from netconn_write() and added sys_sem_new to netconn_new_*.
+
+(STABLE-1_1_1)
+
+ 2006-03-03 Christiaan Simons
+ * ipv4/ip_frag.c: Added bound-checking assertions on ip_reassbitmap
+ access and added pbuf_alloc() return value checks.
+
+ 2006-01-01 Leon Woestenberg <leon.woestenberg@gmx.net>
+ * tcp_{in,out}.c, tcp_out.c: Removed 'even sndbuf' fix in TCP, which is
+ now handled by the checksum routine properly.
+
+ 2006-02-27 Leon Woestenberg <leon.woestenberg@gmx.net>
+ * pbuf.c: Fix alignment; pbuf_init() would not work unless
+ pbuf_pool_memory[] was properly aligned. (Patch by Curt McDowell.)
+
+ 2005-12-20 Leon Woestenberg <leon.woestenberg@gmx.net>
+ * tcp.c: Remove PCBs which stay in LAST_ACK state too long. Patch
+ submitted by Mitrani Hiroshi.
+
+ 2005-12-15 Christiaan Simons
+ * inet.c: Disabled the added summing routine to preserve code space.
+
+ 2005-12-14 Leon Woestenberg <leon.woestenberg@gmx.net>
+ * tcp_in.c: Duplicate FIN ACK race condition fix by Kelvin Lawson.
+ Added Curt McDowell's optimized checksumming routine for future
+ inclusion. Need to create test case for unaliged, aligned, odd,
+ even length combination of cases on various endianess machines.
+
+ 2005-12-09 Christiaan Simons
+ * inet.c: Rewrote standard checksum routine in proper portable C.
+
+ 2005-11-25 Christiaan Simons
+ * udp.c tcp.c: Removed SO_REUSE hack. Should reside in socket code only.
+ * *.c: introduced cc.h LWIP_DEBUG formatters matching the u16_t, s16_t,
+ u32_t, s32_t typedefs. This solves most debug word-length assumes.
+
+ 2005-07-17 Leon Woestenberg <leon.woestenberg@gmx.net>
+ * inet.c: Fixed unaligned 16-bit access in the standard checksum
+ routine by Peter Jolasson.
+ * slipif.c: Fixed implementation assumption of single-pbuf datagrams.
+
+ 2005-02-04 Leon Woestenberg <leon.woestenberg@gmx.net>
+ * tcp_out.c: Fixed uninitialized 'queue' referenced in memerr branch.
+ * tcp_{out|in}.c: Applied patch fixing unaligned access.
+
+ 2005-01-04 Leon Woestenberg <leon.woestenberg@gmx.net>
+ * pbuf.c: Fixed missing semicolon after LWIP_DEBUG statement.
+
+ 2005-01-03 Leon Woestenberg <leon.woestenberg@gmx.net>
+ * udp.c: UDP pcb->recv() was called even when it was NULL.
+
+(STABLE-1_1_0)
+
+ 2004-12-28 Leon Woestenberg <leon.woestenberg@gmx.net>
+ * etharp.*: Disabled multiple packets on the ARP queue.
+ This clashes with TCP queueing.
+
+ 2004-11-28 Leon Woestenberg <leon.woestenberg@gmx.net>
+ * etharp.*: Fixed race condition from ARP request to ARP timeout.
+ Halved the ARP period, doubled the period counts.
+ ETHARP_MAX_PENDING now should be at least 2. This prevents
+ the counter from reaching 0 right away (which would allow
+ too little time for ARP responses to be received).
+
+ 2004-11-25 Leon Woestenberg <leon.woestenberg@gmx.net>
+ * dhcp.c: Decline messages were not multicast but unicast.
+ * etharp.c: ETHARP_CREATE is renamed to ETHARP_TRY_HARD.
+ Do not try hard to insert arbitrary packet's source address,
+ etharp_ip_input() now calls etharp_update() without ETHARP_TRY_HARD.
+ etharp_query() now always DOES call ETHARP_TRY_HARD so that users
+ querying an address will see it appear in the cache (DHCP could
+ suffer from this when a server invalidly gave an in-use address.)
+ * ipv4/ip_addr.h: Renamed ip_addr_maskcmp() to _netcmp() as we are
+ comparing network addresses (identifiers), not the network masks
+ themselves.
+ * ipv4/ip_addr.c: ip_addr_isbroadcast() now checks that the given
+ IP address actually belongs to the network of the given interface.
+
+ 2004-11-24 Kieran Mansley <kjm25@cam.ac.uk>
+ * tcp.c: Increment pcb->snd_buf when ACK is received in SYN_SENT state.
+
+(STABLE-1_1_0-RC1)
+
+ 2004-10-16 Kieran Mansley <kjm25@cam.ac.uk>
+ * tcp.c: Add code to tcp_recved() to send an ACK (window update) immediately,
+ even if one is already pending, if the rcv_wnd is above a threshold
+ (currently TCP_WND/2). This avoids waiting for a timer to expire to send a
+ delayed ACK in order to open the window if the stack is only receiving data.
+
+ 2004-09-12 Kieran Mansley <kjm25@cam.ac.uk>
+ * tcp*.*: Retransmit time-out handling improvement by Sam Jansen.
+
+ 2004-08-20 Tony Mountifield <tony@softins.co.uk>
+ * etharp.c: Make sure the first pbuf queued on an ARP entry
+ is properly ref counted.
+
+ 2004-07-27 Tony Mountifield <tony@softins.co.uk>
+ * debug.h: Added (int) cast in LWIP_DEBUGF() to avoid compiler
+ warnings about comparison.
+ * pbuf.c: Stopped compiler complaining of empty if statement
+ when LWIP_DEBUGF() empty. Closed an unclosed comment.
+ * tcp.c: Stopped compiler complaining of empty if statement
+ when LWIP_DEBUGF() empty.
+ * ip.h Corrected IPH_TOS() macro: returns a byte, so doesn't need htons().
+ * inet.c: Added a couple of casts to quiet the compiler.
+ No need to test isascii(c) before isdigit(c) or isxdigit(c).
+
+ 2004-07-22 Tony Mountifield <tony@softins.co.uk>
+ * inet.c: Made data types consistent in inet_ntoa().
+ Added casts for return values of checksum routines, to pacify compiler.
+ * ip_frag.c, tcp_out.c, sockets.c, pbuf.c
+ Small corrections to some debugging statements, to pacify compiler.
+
+ 2004-07-21 Tony Mountifield <tony@softins.co.uk>
+ * etharp.c: Removed spurious semicolon and added missing end-of-comment.
+ * ethernetif.c Updated low_level_output() to match prototype for
+ netif->linkoutput and changed low_level_input() similarly for consistency.
+ * api_msg.c: Changed recv_raw() from int to u8_t, to match prototype
+ of raw_recv() in raw.h and so avoid compiler error.
+ * sockets.c: Added trivial (int) cast to keep compiler happier.
+ * ip.c, netif.c Changed debug statements to use the tidier ip4_addrN() macros.
+
+(STABLE-1_0_0)
+
+ ++ Changes:
+
+ 2004-07-05 Leon Woestenberg <leon.woestenberg@gmx.net>
+ * sockets.*: Restructured LWIP_PRIVATE_TIMEVAL. Make sure
+ your cc.h file defines this either 1 or 0. If non-defined,
+ defaults to 1.
+ * .c: Added <string.h> and <errno.h> includes where used.
+ * etharp.c: Made some array indices unsigned.
+
+ 2004-06-27 Leon Woestenberg <leon.woestenberg@gmx.net>
+ * netif.*: Added netif_set_up()/down().
+ * dhcp.c: Changes to restart program flow.
+
+ 2004-05-07 Leon Woestenberg <leon.woestenberg@gmx.net>
+ * etharp.c: In find_entry(), instead of a list traversal per candidate, do a
+ single-pass lookup for different candidates. Should exploit locality.
+
+ 2004-04-29 Leon Woestenberg <leon.woestenberg@gmx.net>
+ * tcp*.c: Cleaned up source comment documentation for Doxygen processing.
+ * opt.h: ETHARP_ALWAYS_INSERT option removed to comply with ARP RFC.
+ * etharp.c: update_arp_entry() only adds new ARP entries when adviced to by
+ the caller. This deprecates the ETHARP_ALWAYS_INSERT overrule option.
+
+ ++ Bug fixes:
+
+ 2004-04-27 Leon Woestenberg <leon.woestenberg@gmx.net>
+ * etharp.c: Applied patch of bug #8708 by Toni Mountifield with a solution
+ suggested by Timmy Brolin. Fix for 32-bit processors that cannot access
+ non-aligned 32-bit words, such as soms 32-bit TCP/IP header fields. Fix
+ is to prefix the 14-bit Ethernet headers with two padding bytes.
+
+ 2004-04-23 Leon Woestenberg <leon.woestenberg@gmx.net>
+ * ip_addr.c: Fix in the ip_addr_isbroadcast() check.
+ * etharp.c: Fixed the case where the packet that initiates the ARP request
+ is not queued, and gets lost. Fixed the case where the packets destination
+ address is already known; we now always queue the packet and perform an ARP
+ request.
+
+(STABLE-0_7_0)
+
+ ++ Bug fixes:
+
+ * Fixed TCP bug for SYN_SENT to ESTABLISHED state transition.
+ * Fixed TCP bug in dequeueing of FIN from out of order segment queue.
+ * Fixed two possible NULL references in rare cases.
+
+(STABLE-0_6_6)
+
+ ++ Bug fixes:
+
+ * Fixed DHCP which did not include the IP address in DECLINE messages.
+
+ ++ Changes:
+
+ * etharp.c has been hauled over a bit.
+
+(STABLE-0_6_5)
+
+ ++ Bug fixes:
+
+ * Fixed TCP bug induced by bad window resizing with unidirectional TCP traffic.
+ * Packets sent from ARP queue had invalid source hardware address.
+
+ ++ Changes:
+
+ * Pass-by ARP requests do now update the cache.
+
+ ++ New features:
+
+ * No longer dependent on ctype.h.
+ * New socket options.
+ * Raw IP pcb support.
+
+(STABLE-0_6_4)
+
+ ++ Bug fixes:
+
+ * Some debug formatters and casts fixed.
+ * Numereous fixes in PPP.
+
+ ++ Changes:
+
+ * DEBUGF now is LWIP_DEBUGF
+ * pbuf_dechain() has been re-enabled.
+ * Mentioned the changed use of CVS branches in README.
+
+(STABLE-0_6_3)
+
+ ++ Bug fixes:
+
+ * Fixed pool pbuf memory leak in pbuf_alloc().
+ Occured if not enough PBUF_POOL pbufs for a packet pbuf chain.
+ Reported by Savin Zlobec.
+
+ * PBUF_POOL chains had their tot_len field not set for non-first
+ pbufs. Fixed in pbuf_alloc().
+
+ ++ New features:
+
+ * Added PPP stack contributed by Marc Boucher
+
+ ++ Changes:
+
+ * Now drops short packets for ICMP/UDP/TCP protocols. More robust.
+
+ * ARP queueuing now queues the latest packet instead of the first.
+ This is the RFC recommended behaviour, but can be overridden in
+ lwipopts.h.
+
+(0.6.2)
+
+ ++ Bugfixes:
+
+ * TCP has been fixed to deal with the new use of the pbuf->ref
+ counter.
+
+ * DHCP dhcp_inform() crash bug fixed.
+
+ ++ Changes:
+
+ * Removed pbuf_pool_free_cache and pbuf_pool_alloc_cache. Also removed
+ pbuf_refresh(). This has sped up pbuf pool operations considerably.
+ Implemented by David Haas.
+
+(0.6.1)
+
+ ++ New features:
+
+ * The packet buffer implementation has been enhanced to support
+ zero-copy and copy-on-demand for packet buffers which have their
+ payloads in application-managed memory.
+ Implemented by David Haas.
+
+ Use PBUF_REF to make a pbuf refer to RAM. lwIP will use zero-copy
+ if an outgoing packet can be directly sent on the link, or perform
+ a copy-on-demand when necessary.
+
+ The application can safely assume the packet is sent, and the RAM
+ is available to the application directly after calling udp_send()
+ or similar function.
+
+ ++ Bugfixes:
+
+ * ARP_QUEUEING should now correctly work for all cases, including
+ PBUF_REF.
+ Implemented by Leon Woestenberg.
+
+ ++ Changes:
+
+ * IP_ADDR_ANY is no longer a NULL pointer. Instead, it is a pointer
+ to a '0.0.0.0' IP address.
+
+ * The packet buffer implementation is changed. The pbuf->ref counter
+ meaning has changed, and several pbuf functions have been
+ adapted accordingly.
+
+ * netif drivers have to be changed to set the hardware address length field
+ that must be initialized correctly by the driver (hint: 6 for Ethernet MAC).
+ See the contrib/ports/c16x cs8900 driver as a driver example.
+
+ * netif's have a dhcp field that must be initialized to NULL by the driver.
+ See the contrib/ports/c16x cs8900 driver as a driver example.
+
+(0.5.x) This file has been unmaintained up to 0.6.1. All changes are
+ logged in CVS but have not been explained here.
+
+(0.5.3) Changes since version 0.5.2
+
+ ++ Bugfixes:
+
+ * memp_malloc(MEMP_API_MSG) could fail with multiple application
+ threads because it wasn't protected by semaphores.
+
+ ++ Other changes:
+
+ * struct ip_addr now packed.
+
+ * The name of the time variable in arp.c has been changed to ctime
+ to avoid conflicts with the time() function.
+
+(0.5.2) Changes since version 0.5.1
+
+ ++ New features:
+
+ * A new TCP function, tcp_tmr(), now handles both TCP timers.
+
+ ++ Bugfixes:
+
+ * A bug in tcp_parseopt() could cause the stack to hang because of a
+ malformed TCP option.
+
+ * The address of new connections in the accept() function in the BSD
+ socket library was not handled correctly.
+
+ * pbuf_dechain() did not update the ->tot_len field of the tail.
+
+ * Aborted TCP connections were not handled correctly in all
+ situations.
+
+ ++ Other changes:
+
+ * All protocol header structs are now packed.
+
+ * The ->len field in the tcp_seg structure now counts the actual
+ amount of data, and does not add one for SYN and FIN segments.
+
+(0.5.1) Changes since version 0.5.0
+
+ ++ New features:
+
+ * Possible to run as a user process under Linux.
+
+ * Preliminary support for cross platform packed structs.
+
+ * ARP timer now implemented.
+
+ ++ Bugfixes:
+
+ * TCP output queue length was badly initialized when opening
+ connections.
+
+ * TCP delayed ACKs were not sent correctly.
+
+ * Explicit initialization of BSS segment variables.
+
+ * read() in BSD socket library could drop data.
+
+ * Problems with memory alignment.
+
+ * Situations when all TCP buffers were used could lead to
+ starvation.
+
+ * TCP MSS option wasn't parsed correctly.
+
+ * Problems with UDP checksum calculation.
+
+ * IP multicast address tests had endianess problems.
+
+ * ARP requests had wrong destination hardware address.
+
+ ++ Other changes:
+
+ * struct eth_addr changed from u16_t[3] array to u8_t[6].
+
+ * A ->linkoutput() member was added to struct netif.
+
+ * TCP and UDP ->dest_* struct members where changed to ->remote_*.
+
+ * ntoh* macros are now null definitions for big endian CPUs.
+
+(0.5.0) Changes since version 0.4.2
+
+ ++ New features:
+
+ * Redesigned operating system emulation layer to make porting easier.
+
+ * Better control over TCP output buffers.
+
+ * Documenation added.
+
+ ++ Bugfixes:
+
+ * Locking issues in buffer management.
+
+ * Bugfixes in the sequential API.
+
+ * IP forwarding could cause memory leakage. This has been fixed.
+
+ ++ Other changes:
+
+ * Directory structure somewhat changed; the core/ tree has been
+ collapsed.
+
+(0.4.2) Changes since version 0.4.1
+
+ ++ New features:
+
+ * Experimental ARP implementation added.
+
+ * Skeleton Ethernet driver added.
+
+ * Experimental BSD socket API library added.
+
+ ++ Bugfixes:
+
+ * In very intense situations, memory leakage could occur. This has
+ been fixed.
+
+ ++ Other changes:
+
+ * Variables named "data" and "code" have been renamed in order to
+ avoid name conflicts in certain compilers.
+
+ * Variable++ have in appliciable cases been translated to ++variable
+ since some compilers generate better code in the latter case.
+
+(0.4.1) Changes since version 0.4
+
+ ++ New features:
+
+ * TCP: Connection attempts time out earlier than data
+ transmissions. Nagle algorithm implemented. Push flag set on the
+ last segment in a burst.
+
+ * UDP: experimental support for UDP-Lite extensions.
+
+ ++ Bugfixes:
+
+ * TCP: out of order segments were in some cases handled incorrectly,
+ and this has now been fixed. Delayed acknowledgements was broken
+ in 0.4, has now been fixed. Binding to an address that is in use
+ now results in an error. Reset connections sometimes hung an
+ application; this has been fixed.
+
+ * Checksum calculation sometimes failed for chained pbufs with odd
+ lengths. This has been fixed.
+
+ * API: a lot of bug fixes in the API. The UDP API has been improved
+ and tested. Error reporting and handling has been
+ improved. Logical flaws and race conditions for incoming TCP
+ connections has been found and removed.
+
+ * Memory manager: alignment issues. Reallocating memory sometimes
+ failed, this has been fixed.
+
+ * Generic library: bcopy was flawed and has been fixed.
+
+ ++ Other changes:
+
+ * API: all datatypes has been changed from generic ones such as
+ ints, to specified ones such as u16_t. Functions that return
+ errors now have the correct type (err_t).
+
+ * General: A lot of code cleaned up and debugging code removed. Many
+ portability issues have been fixed.
+
+ * The license was changed; the advertising clause was removed.
+
+ * C64 port added.
+
+ * Thanks: Huge thanks go to Dagan Galarneau, Horst Garnetzke, Petri
+ Kosunen, Mikael Caleres, and Frits Wilmink for reporting and
+ fixing bugs!
+
+(0.4) Changes since version 0.3.1
+
+ * Memory management has been radically changed; instead of
+ allocating memory from a shared heap, memory for objects that are
+ rapidly allocated and deallocated is now kept in pools. Allocation
+ and deallocation from those memory pools is very fast. The shared
+ heap is still present but is used less frequently.
+
+ * The memory, memory pool, and packet buffer subsystems now support
+ 4-, 2-, or 1-byte alignment.
+
+ * "Out of memory" situations are handled in a more robust way.
+
+ * Stack usage has been reduced.
+
+ * Easier configuration of lwIP parameters such as memory usage,
+ TTLs, statistics gathering, etc. All configuration parameters are
+ now kept in a single header file "lwipopts.h".
+
+ * The directory structure has been changed slightly so that all
+ architecture specific files are kept under the src/arch
+ hierarchy.
+
+ * Error propagation has been improved, both in the protocol modules
+ and in the API.
+
+ * The code for the RTXC architecture has been implemented, tested
+ and put to use.
+
+ * Bugs have been found and corrected in the TCP, UDP, IP, API, and
+ the Internet checksum modules.
+
+ * Bugs related to porting between a 32-bit and a 16-bit architecture
+ have been found and corrected.
+
+ * The license has been changed slightly to conform more with the
+ original BSD license, including the advertisement clause.
+
+(0.3.1) Changes since version 0.3
+
+ * Fix of a fatal bug in the buffer management. Pbufs with allocated
+ RAM never returned the RAM when the pbuf was deallocated.
+
+ * TCP congestion control, window updates and retransmissions did not
+ work correctly. This has now been fixed.
+
+ * Bugfixes in the API.
+
+(0.3) Changes since version 0.2
+
+ * New and improved directory structure. All include files are now
+ kept in a dedicated include/ directory.
+
+ * The API now has proper error handling. A new function,
+ netconn_err(), now returns an error code for the connection in
+ case of errors.
+
+ * Improvements in the memory management subsystem. The system now
+ keeps a pointer to the lowest free memory block. A new function,
+ mem_malloc2() tries to allocate memory once, and if it fails tries
+ to free some memory and retry the allocation.
+
+ * Much testing has been done with limited memory
+ configurations. lwIP now does a better job when overloaded.
+
+ * Some bugfixes and improvements to the buffer (pbuf) subsystem.
+
+ * Many bugfixes in the TCP code:
+
+ - Fixed a bug in tcp_close().
+
+ - The TCP receive window was incorrectly closed when out of
+ sequence segments was received. This has been fixed.
+
+ - Connections are now timed-out of the FIN-WAIT-2 state.
+
+ - The initial congestion window could in some cases be too
+ large. This has been fixed.
+
+ - The retransmission queue could in some cases be screwed up. This
+ has been fixed.
+
+ - TCP RST flag now handled correctly.
+
+ - Out of sequence data was in some cases never delivered to the
+ application. This has been fixed.
+
+ - Retransmitted segments now contain the correct acknowledgment
+ number and advertised window.
+
+ - TCP retransmission timeout backoffs are not correctly computed
+ (ala BSD). After a number of retransmissions, TCP now gives up
+ the connection.
+
+ * TCP connections now are kept on three lists, one for active
+ connections, one for listening connections, and one for
+ connections that are in TIME-WAIT. This greatly speeds up the fast
+ timeout processing for sending delayed ACKs.
+
+ * TCP now provides proper feedback to the application when a
+ connection has been successfully set up.
+
+ * More comments have been added to the code. The code has also been
+ somewhat cleaned up.
+
+(0.2) Initial public release.
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/COPYING b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/COPYING
new file mode 100644
index 0000000..e23898b
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/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/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/FILES b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/FILES
new file mode 100644
index 0000000..e6e0998
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/FILES
@@ -0,0 +1,5 @@
+src/ - The source code for the lwIP TCP/IP stack.
+doc/ - The documentation for lwIP.
+test/ - Some code to test whether the sources do what they should.
+
+See also the FILES file in each subdirectory.
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/README b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/README
new file mode 100644
index 0000000..0884d27
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/README
@@ -0,0 +1,100 @@
+INTRODUCTION
+
+lwIP is a small independent implementation of the TCP/IP protocol
+suite that has been developed by Adam Dunkels at the Computer and
+Networks Architectures (CNA) lab at the Swedish Institute of Computer
+Science (SICS).
+
+The focus of the lwIP TCP/IP implementation is to reduce the RAM usage
+while still having a full scale TCP. This making lwIP suitable for use
+in embedded systems with tens of kilobytes of free RAM and room for
+around 40 kilobytes of code ROM.
+
+
+FEATURES
+
+ * IP (Internet Protocol, IPv4 and IPv6) including packet forwarding over
+ multiple network interfaces
+ * ICMP (Internet Control Message Protocol) for network maintenance and debugging
+ * IGMP (Internet Group Management Protocol) for multicast traffic management
+ * MLD (Multicast listener discovery for IPv6). Aims to be compliant with
+ RFC 2710. No support for MLDv2
+ * ND (Neighbor discovery and stateless address autoconfiguration for IPv6).
+ Aims to be compliant with RFC 4861 (Neighbor discovery) and RFC 4862
+ (Address autoconfiguration)
+ * UDP (User Datagram Protocol) including experimental UDP-lite extensions
+ * TCP (Transmission Control Protocol) with congestion control, RTT estimation
+ and fast recovery/fast retransmit
+ * raw/native API for enhanced performance
+ * Optional Berkeley-like socket API
+ * DNS (Domain names resolver)
+
+
+APPLICATIONS
+
+ * HTTP server with SSI and CGI
+ * SNMPv2c agent with MIB compiler (Simple Network Management Protocol)
+ * SNTP (Simple network time protocol)
+ * NetBIOS name service responder
+ * MDNS (Multicast DNS) responder
+ * iPerf server implementation
+
+
+LICENSE
+
+lwIP is freely available under a BSD license.
+
+
+DEVELOPMENT
+
+lwIP has grown into an excellent TCP/IP stack for embedded devices,
+and developers using the stack often submit bug fixes, improvements,
+and additions to the stack to further increase its usefulness.
+
+Development of lwIP is hosted on Savannah, a central point for
+software development, maintenance and distribution. Everyone can
+help improve lwIP by use of Savannah's interface, Git and the
+mailing list. A core team of developers will commit changes to the
+Git source tree.
+
+The lwIP TCP/IP stack is maintained in the 'lwip' Git module and
+contributions (such as platform ports) are in the 'contrib' Git module.
+
+See doc/savannah.txt for details on Git server access for users and
+developers.
+
+The current Git trees are web-browsable:
+ http://git.savannah.gnu.org/cgit/lwip.git
+ http://git.savannah.gnu.org/cgit/lwip/lwip-contrib.git
+
+Submit patches and bugs via the lwIP project page:
+ http://savannah.nongnu.org/projects/lwip/
+
+Continuous integration builds (GCC, clang):
+ https://travis-ci.org/yarrick/lwip-merged
+
+
+DOCUMENTATION
+
+Self documentation of the source code is regularly extracted from the current
+Git sources and is available from this web page:
+ http://www.nongnu.org/lwip/
+
+There is now a constantly growing wiki about lwIP at
+ http://lwip.wikia.com/wiki/LwIP_Wiki
+
+Also, there are mailing lists you can subscribe at
+ http://savannah.nongnu.org/mail/?group=lwip
+plus searchable archives:
+ http://lists.nongnu.org/archive/html/lwip-users/
+ http://lists.nongnu.org/archive/html/lwip-devel/
+
+lwIP was originally written by Adam Dunkels:
+ http://dunkels.com/adam/
+
+Reading Adam's papers, the files in docs/, browsing the source code
+documentation and browsing the mailing list archives is a good way to
+become familiar with the design of lwIP.
+
+Adam Dunkels <adam@sics.se>
+Leon Woestenberg <leon.woestenberg@gmx.net>
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/UPGRADING b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/UPGRADING
new file mode 100644
index 0000000..3a984c9
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/UPGRADING
@@ -0,0 +1,235 @@
+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.
+
+
+(git master)
+
+ * [Enter new changes just after this line - do not remove this line]
+
+(2.0.1)
+
+ ++ Application changes:
+
+ * UDP does NOT receive multicast traffic from ALL netifs on an UDP PCB bound to a specific
+ netif any more. Users need to bind to IP_ADDR_ANY to receive multicast traffic and compare
+ ip_current_netif() to the desired netif for every packet.
+ See bug #49662 for an explanation.
+
+(2.0.0)
+
+ ++ Application changes:
+
+ * Changed netif "up" flag handling to be an administrative flag (as opposed to the previous meaning of
+ "ip4-address-valid", a netif will now not be used for transmission if not up) -> even a DHCP netif
+ has to be set "up" before starting the DHCP client
+ * Added IPv6 support (dual-stack or IPv4/IPv6 only)
+ * Changed ip_addr_t to be a union in dual-stack mode (use ip4_addr_t where referring to IPv4 only).
+ * Major rewrite of SNMP (added MIB parser that creates code stubs for custom MIBs);
+ supports SNMPv2c (experimental v3 support)
+ * Moved some core applications from contrib repository to src/apps (and include/lwip/apps)
+
+ +++ Raw API:
+ * Changed TCP listen backlog: removed tcp_accepted(), added the function pair tcp_backlog_delayed()/
+ tcp_backlog_accepted() to explicitly delay backlog handling on a connection pcb
+
+ +++ Socket API:
+ * Added an implementation for posix sendmsg()
+ * Added LWIP_FIONREAD_LINUXMODE that makes ioctl/FIONREAD return the size of the next pending datagram
+
+ ++ Port changes
+
+ +++ new files:
+ * MANY new and moved files!
+ * Added src/Filelists.mk for use in Makefile projects
+ * Continued moving stack-internal parts from abc.h to abc_priv.h in sub-folder "priv"
+ to let abc.h only contain the actual application programmer's API
+
+ +++ sys layer:
+ * Made LWIP_TCPIP_CORE_LOCKING==1 the default as it usually performs better than
+ the traditional message passing (although with LWIP_COMPAT_MUTEX you are still
+ open to priority inversion, so this is not recommended any more)
+ * Added LWIP_NETCONN_SEM_PER_THREAD to use one "op_completed" semaphore per thread
+ instead of using one per netconn (these semaphores are used even with core locking
+ enabled as some longer lasting functions like big writes still need to delay)
+ * Added generalized abstraction for itoa(), strnicmp(), stricmp() and strnstr()
+ in def.h (to be overridden in cc.h) instead of config
+ options for netbiosns, httpd, dns, etc. ...
+ * New abstraction for hton* and ntoh* functions in def.h.
+ To override them, use the following in cc.h:
+ #define lwip_htons(x) <your_htons>
+ #define lwip_htonl(x) <your_htonl>
+
+ +++ new options:
+ * TODO
+
+ +++ new pools:
+ * Added LWIP_MEMPOOL_* (declare/init/alloc/free) to declare private memp pools
+ that share memp.c code but do not have to be made global via lwippools.h
+ * Added pools for IPv6, MPU_COMPATIBLE, dns-api, netif-api, etc.
+ * added hook LWIP_HOOK_MEMP_AVAILABLE() to get informed when a memp pool was empty and an item
+ is now available
+
+ * Signature of LWIP_HOOK_VLAN_SET macro was changed
+
+ * LWIP_DECLARE_MEMORY_ALIGNED() may be used to declare aligned memory buffers (mem/memp)
+ or to move buffers to dedicated memory using compiler attributes
+
+ * Standard C headers are used to define sized types and printf formatters
+ (disable by setting LWIP_NO_STDINT_H=1 or LWIP_NO_INTTYPES_H=1 if your compiler
+ does not support these)
+
+
+ ++ Major bugfixes/improvements
+
+ * Added IPv6 support (dual-stack or IPv4/IPv6 only)
+ * Major rewrite of PPP (incl. keep-up with apache pppd)
+ see doc/ppp.txt for an upgrading how-to
+ * Major rewrite of SNMP (incl. MIB parser)
+ * Fixed timing issues that might have lead to losing a DHCP lease
+ * Made rx processing path more robust against crafted errors
+ * TCP window scaling support
+ * modification of api modules to support FreeRTOS-MPU (don't pass stack-pointers to other threads)
+ * made DNS client more robust
+ * support PBUF_REF for RX packets
+ * LWIP_NETCONN_FULLDUPLEX allows netconn/sockets to be used for reading/writing from separate
+ threads each (needs LWIP_NETCONN_SEM_PER_THREAD)
+ * Moved and reordered stats (mainly memp/mib2)
+
+(1.4.0)
+
+ ++ 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/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/doc/FILES b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/doc/FILES
new file mode 100644
index 0000000..e588575
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/doc/FILES
@@ -0,0 +1,9 @@
+doxygen/ - Configuration files and scripts to create the lwIP doxygen source
+ documentation (found at http://www.nongnu.org/lwip/)
+
+savannah.txt - How to obtain the current development source code.
+contrib.txt - How to contribute to lwIP as a developer.
+rawapi.txt - The documentation for the core API of lwIP.
+ Also provides an overview about the other APIs and multithreading.
+sys_arch.txt - The documentation for a system abstraction layer of lwIP.
+ppp.txt - Documentation of the PPP interface for lwIP.
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/doc/NO_SYS_SampleCode.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/doc/NO_SYS_SampleCode.c
new file mode 100644
index 0000000..f5c6c10
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/doc/NO_SYS_SampleCode.c
@@ -0,0 +1,117 @@
+void eth_mac_irq()
+{
+ /* Service MAC IRQ here */
+
+ /* Allocate pbuf from pool (avoid using heap in interrupts) */
+ struct pbuf* p = pbuf_alloc(PBUF_RAW, eth_data_count, PBUF_POOL);
+
+ if(p != NULL) {
+ /* Copy ethernet frame into pbuf */
+ pbuf_take(p, eth_data, eth_data_count);
+
+ /* Put in a queue which is processed in main loop */
+ if(!queue_try_put(&queue, p)) {
+ /* queue is full -> packet loss */
+ pbuf_free(p);
+ }
+ }
+}
+
+static err_t netif_output(struct netif *netif, struct pbuf *p)
+{
+ LINK_STATS_INC(link.xmit);
+
+ /* Update SNMP stats (only if you use SNMP) */
+ MIB2_STATS_NETIF_ADD(netif, ifoutoctets, p->tot_len);
+ int unicast = ((p->payload[0] & 0x01) == 0);
+ if (unicast) {
+ MIB2_STATS_NETIF_INC(netif, ifoutucastpkts);
+ } else {
+ MIB2_STATS_NETIF_INC(netif, ifoutnucastpkts);
+ }
+
+ lock_interrupts();
+ pbuf_copy_partial(p, mac_send_buffer, p->tot_len, 0);
+ /* Start MAC transmit here */
+ unlock_interrupts();
+
+ return ERR_OK;
+}
+
+static void netif_status_callback(struct netif *netif)
+{
+ printf("netif status changed %s\n", ip4addr_ntoa(netif_ip4_addr(netif)));
+}
+
+static err_t netif_init(struct netif *netif)
+{
+ netif->linkoutput = netif_output;
+ netif->output = etharp_output;
+ netif->output_ip6 = ethip6_output;
+ netif->mtu = ETHERNET_MTU;
+ netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET | NETIF_FLAG_IGMP | NETIF_FLAG_MLD6;
+ MIB2_INIT_NETIF(netif, snmp_ifType_ethernet_csmacd, 100000000);
+
+ SMEMCPY(netif->hwaddr, your_mac_address_goes_here, sizeof(netif->hwaddr));
+ netif->hwaddr_len = sizeof(netif->hwaddr);
+
+ return ERR_OK;
+}
+
+void main(void)
+{
+ struct netif netif;
+
+ lwip_init();
+
+ netif_add(&netif, IP4_ADDR_ANY, IP4_ADDR_ANY, IP4_ADDR_ANY, NULL, netif_init, netif_input);
+ netif.name[0] = 'e';
+ netif.name[1] = '0';
+ netif_create_ip6_linklocal_address(&netif, 1);
+ netif.ip6_autoconfig_enabled = 1;
+ netif_set_status_callback(&netif, netif_status_callback);
+ netif_set_default(&netif);
+ netif_set_up(&netif);
+
+ /* Start DHCP and HTTPD */
+ dhcp_init();
+ httpd_init();
+
+ while(1) {
+ /* Check link state, e.g. via MDIO communication with PHY */
+ if(link_state_changed()) {
+ if(link_is_up()) {
+ netif_set_link_up(&netif);
+ } else {
+ netif_set_link_down(&netif);
+ }
+ }
+
+ /* Check for received frames, feed them to lwIP */
+ lock_interrupts();
+ struct pbuf* p = queue_try_get(&queue);
+ unlock_interrupts();
+
+ if(p != NULL) {
+ LINK_STATS_INC(link.recv);
+
+ /* Update SNMP stats (only if you use SNMP) */
+ MIB2_STATS_NETIF_ADD(netif, ifinoctets, p->tot_len);
+ int unicast = ((p->payload[0] & 0x01) == 0);
+ if (unicast) {
+ MIB2_STATS_NETIF_INC(netif, ifinucastpkts);
+ } else {
+ MIB2_STATS_NETIF_INC(netif, ifinnucastpkts);
+ }
+
+ if(netif.input(p, &netif) != ERR_OK) {
+ pbuf_free(p);
+ }
+ }
+
+ /* Cyclic lwIP timers check */
+ sys_check_timeouts();
+
+ /* your application goes here */
+ }
+}
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/doc/contrib.txt b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/doc/contrib.txt
new file mode 100644
index 0000000..6f0d7bc
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/doc/contrib.txt
@@ -0,0 +1,58 @@
+1 Introduction
+
+This document describes some guidelines for people participating
+in lwIP development.
+
+2 How to contribute to lwIP
+
+Here is a short list of suggestions to anybody working with lwIP and
+trying to contribute bug reports, fixes, enhancements, platform ports etc.
+First of all as you may already know lwIP is a volunteer project so feedback
+to fixes or questions might often come late. Hopefully the bug and patch tracking
+features of Savannah help us not lose users' input.
+
+2.1 Source code style:
+
+1. do not use tabs.
+2. indentation is two spaces per level (i.e. per tab).
+3. end debug messages with a trailing newline (\n).
+4. one space between keyword and opening bracket.
+5. no space between function and opening bracket.
+6. one space and no newline before opening curly braces of a block.
+7. closing curly brace on a single line.
+8. spaces surrounding assignment and comparisons.
+9. don't initialize static and/or global variables to zero, the compiler takes care of that.
+10. use current source code style as further reference.
+
+2.2 Source code documentation style:
+
+1. JavaDoc compliant and Doxygen compatible.
+2. Function documentation above functions in .c files, not .h files.
+ (This forces you to synchronize documentation and implementation.)
+3. Use current documentation style as further reference.
+
+2.3 Bug reports and patches:
+
+1. Make sure you are reporting bugs or send patches against the latest
+ sources. (From the latest release and/or the current Git sources.)
+2. If you think you found a bug make sure it's not already filed in the
+ bugtracker at Savannah.
+3. If you have a fix put the patch on Savannah. If it is a patch that affects
+ both core and arch specific stuff please separate them so that the core can
+ be applied separately while leaving the other patch 'open'. The preferred way
+ is to NOT touch archs you can't test and let maintainers take care of them.
+ This is a good way to see if they are used at all - the same goes for unix
+ netifs except tapif.
+4. Do not file a bug and post a fix to it to the patch area. Either a bug report
+ or a patch will be enough.
+ If you correct an existing bug then attach the patch to the bug rather than creating a new entry in the patch area.
+5. Patches should be specific to a single change or to related changes. Do not mix bugfixes with spelling and other
+ trivial fixes unless the bugfix is trivial too. Do not reorganize code and rename identifiers in the same patch you
+ change behaviour if not necessary. A patch is easier to read and understand if it's to the point and short than
+ if it's not to the point and long :) so the chances for it to be applied are greater.
+
+2.4 Platform porters:
+
+1. If you have ported lwIP to a platform (an OS, a uC/processor or a combination of these) and
+ you think it could benefit others[1] you might want discuss this on the mailing list. You
+ can also ask for Git access to submit and maintain your port in the contrib Git module.
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/doc/doxygen/generate.bat b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/doc/doxygen/generate.bat
new file mode 100644
index 0000000..99afb12
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/doc/doxygen/generate.bat
@@ -0,0 +1 @@
+doxygen lwip.Doxyfile
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/doc/doxygen/generate.sh b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/doc/doxygen/generate.sh
new file mode 100644
index 0000000..89344b0
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/doc/doxygen/generate.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+doxygen lwip.Doxyfile
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/doc/doxygen/lwip.Doxyfile b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/doc/doxygen/lwip.Doxyfile
new file mode 100644
index 0000000..0e3349a
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/doc/doxygen/lwip.Doxyfile
@@ -0,0 +1,2505 @@
+# Doxyfile 1.8.11
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project.
+#
+# All text after a double hash (##) is considered a comment and is placed in
+# front of the TAG it is preceding.
+#
+# All text after a single 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.
+# The default value is: UTF-8.
+
+DOXYFILE_ENCODING = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by
+# double-quotes, unless you are using Doxywizard) that should identify the
+# project for which the documentation is generated. This name is used in the
+# title of most generated pages and in a few other places.
+# The default value is: My Project.
+
+PROJECT_NAME = "lwIP"
+
+# 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 = "2.0.2"
+
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer a
+# quick idea about the purpose of the project. Keep the description short.
+
+PROJECT_BRIEF = "Lightweight IP stack"
+
+# With the PROJECT_LOGO tag one can specify a logo or an icon that is included
+# in the documentation. The maximum height of the logo should not exceed 55
+# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy
+# the logo to the output directory.
+
+PROJECT_LOGO =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
+# into which the generated documentation will be written. 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 = output
+
+# 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 causes
+# performance problems for the file system.
+# The default value is: NO.
+
+CREATE_SUBDIRS = NO
+
+# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII
+# characters to appear in the names of generated files. If set to NO, non-ASCII
+# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode
+# U+3044.
+# The default value is: NO.
+
+ALLOW_UNICODE_NAMES = 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.
+# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese,
+# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),
+# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian,
+# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages),
+# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian,
+# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian,
+# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish,
+# Ukrainian and Vietnamese.
+# The default value is: English.
+
+OUTPUT_LANGUAGE = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES, 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.
+# The default value is: YES.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES, 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.
+# The default value is: YES.
+
+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 and the.
+
+ABBREVIATE_BRIEF = "The $name class " \
+ "The $name widget " \
+ "The $name file " \
+ is \
+ provides \
+ specifies \
+ contains \
+ represents \
+ a \
+ an \
+ the
+
+# 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.
+# The default value is: NO.
+
+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.
+# The default value is: NO.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES, 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
+# The default value is: YES.
+
+FULL_PATH_NAMES = YES
+
+# 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.
+#
+# Note that you can specify absolute paths here, but also relative paths, which
+# will be relative from the directory where doxygen is started.
+# This tag requires that the tag FULL_PATH_NAMES is set to YES.
+
+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 list of 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.
+# The default value is: NO.
+
+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-style will behave just like regular Qt-
+# style comments (thus requiring an explicit @brief command for a brief
+# description.)
+# The default value is: NO.
+
+JAVADOC_AUTOBRIEF = NO
+
+# 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 Qt-style will behave just like regular Qt-style comments (thus
+# requiring an explicit \brief command for a brief description.)
+# The default value is: NO.
+
+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 behavior. 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 behavior instead.
+#
+# Note that setting this tag to YES also means that rational rose comments are
+# not recognized any more.
+# The default value is: NO.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
+# documentation from any documented member that it re-implements.
+# The default value is: YES.
+
+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.
+# The default value is: NO.
+
+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.
+# Minimum value: 1, maximum value: 16, default value: 4.
+
+TAB_SIZE = 8
+
+# This tag can be used to specify a number of aliases that act 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 =
+
+# This tag can be used to specify a number of word-keyword mappings (TCL only).
+# A mapping has the form "name=value". For example adding "class=itcl::class"
+# will allow you to use the command class in the itcl::class meaning.
+
+TCL_SUBST =
+
+# 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.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_FOR_C = YES
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or
+# Python sources only. Doxygen will then generate output that is more tailored
+# for that language. For instance, namespaces will be presented as packages,
+# qualified scopes will look different, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources. Doxygen will then generate output that is tailored for Fortran.
+# The default value is: NO.
+
+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.
+# The default value is: NO.
+
+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,
+# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran:
+# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran:
+# Fortran. In the later case the parser tries to guess whether the code is fixed
+# or free formatted code, this is the default for Fortran type files), VHDL. 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: For files without extension you can use no_extension as a placeholder.
+#
+# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
+# the files are not read by doxygen.
+
+EXTENSION_MAPPING =
+
+# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
+# according to the Markdown format, which allows for more readable
+# documentation. See http://daringfireball.net/projects/markdown/ for details.
+# The output of markdown processing is further processed by doxygen, so you can
+# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
+# case of backward compatibilities issues.
+# The default value is: YES.
+
+MARKDOWN_SUPPORT = YES
+
+# When enabled doxygen tries to link words that correspond to documented
+# classes, or namespaces to their corresponding documentation. Such a link can
+# be prevented in individual cases by putting a % sign in front of the word or
+# globally by setting AUTOLINK_SUPPORT to NO.
+# The default value is: YES.
+
+AUTOLINK_SUPPORT = YES
+
+# 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);
+# versus func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+# The default value is: NO.
+
+BUILTIN_STL_SUPPORT = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+# The default value is: NO.
+
+CPP_CLI_SUPPORT = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
+# http://www.riverbankcomputing.co.uk/software/sip/intro) 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.
+# The default value is: NO.
+
+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 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.
+# The default value is: YES.
+
+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.
+# The default value is: NO.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# If one adds a struct or class to a group and this option is enabled, then also
+# any nested class or struct is added to the same group. By default this option
+# is disabled and one has to add nested compounds explicitly via \ingroup.
+# The default value is: NO.
+
+GROUP_NESTED_COMPOUNDS = NO
+
+# Set the SUBGROUPING tag to YES 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.
+# The default value is: YES.
+
+SUBGROUPING = YES
+
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions
+# are shown inside the group in which they are included (e.g. using \ingroup)
+# instead of on a separate page (for HTML and Man pages) or section (for LaTeX
+# and RTF).
+#
+# Note that this feature does not work in combination with
+# SEPARATE_MEMBER_PAGES.
+# The default value is: NO.
+
+INLINE_GROUPED_CLASSES = NO
+
+# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions
+# with only public data fields or simple typedef fields will be shown inline in
+# the documentation of the scope in which they are defined (i.e. file,
+# namespace, or group documentation), provided this scope is documented. If set
+# to NO, structs, classes, and unions are shown on a separate page (for HTML and
+# Man pages) or section (for LaTeX and RTF).
+# The default value is: NO.
+
+INLINE_SIMPLE_STRUCTS = NO
+
+# When TYPEDEF_HIDES_STRUCT tag 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.
+# The default value is: NO.
+
+TYPEDEF_HIDES_STRUCT = NO
+
+# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This
+# cache is used to resolve symbols given their name and scope. Since this can be
+# an expensive process and often the same symbol appears multiple times in the
+# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small
+# doxygen will become slower. If the cache is too large, memory is wasted. The
+# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range
+# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536
+# symbols. At the end of a run doxygen will report the cache usage and suggest
+# the optimal cache size from a speed point of view.
+# Minimum value: 0, maximum value: 9, default value: 0.
+
+LOOKUP_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 respectively EXTRACT_STATIC tags are set to YES.
+# Note: This will also disable the warnings about undocumented members that are
+# normally produced when WARNINGS is set to YES.
+# The default value is: NO.
+
+EXTRACT_ALL = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will
+# be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PRIVATE = NO
+
+# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal
+# scope will be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PACKAGE = NO
+
+# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be
+# included in the documentation.
+# The default value is: NO.
+
+EXTRACT_STATIC = NO
+
+# 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. Does not have any effect
+# for Java sources.
+# The default value is: YES.
+
+EXTRACT_LOCAL_CLASSES = YES
+
+# This flag is only useful for Objective-C code. If 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, only methods in the interface are
+# included.
+# The default value is: NO.
+
+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.
+# The default value is: NO.
+
+EXTRACT_ANON_NSPACES = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
+# undocumented members inside documented classes or files. If set to NO 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.
+# The default value is: NO.
+
+HIDE_UNDOC_MEMBERS = YES
+
+# 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, these classes will be included in the various overviews. This option
+# has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_CLASSES = YES
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
+# (class|struct|union) declarations. If set to NO, these declarations will be
+# included in the documentation.
+# The default value is: NO.
+
+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, these
+# blocks will be appended to the function's detailed documentation block.
+# The default value is: NO.
+
+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 then the documentation
+# will be excluded. Set it to YES to include the internal documentation.
+# The default value is: NO.
+
+INTERNAL_DOCS = NO
+
+# 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.
+# The default value is: system dependent.
+
+CASE_SENSE_NAMES = NO
+
+# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with
+# their full class and namespace scopes in the documentation. If set to YES, the
+# scope will be hidden.
+# The default value is: NO.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will
+# append additional text to a page's title, such as Class Reference. If set to
+# YES the compound reference will be hidden.
+# The default value is: NO.
+
+HIDE_COMPOUND_REFERENCE= NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of
+# the files that are included by a file in the documentation of that file.
+# The default value is: YES.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each
+# grouped member an include statement to the documentation, telling the reader
+# which file to include in order to use the member.
+# The default value is: NO.
+
+SHOW_GROUPED_MEMB_INC = NO
+
+# 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.
+# The default value is: NO.
+
+FORCE_LOCAL_INCLUDES = NO
+
+# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the
+# documentation for inline members.
+# The default value is: YES.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES 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.
+# The default value is: YES.
+
+SORT_MEMBER_DOCS = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief
+# descriptions of file, namespace and class members alphabetically by member
+# name. If set to NO, the members will appear in declaration order. Note that
+# this will also influence the order of the classes in the class list.
+# The default value is: NO.
+
+SORT_BRIEF_DOCS = NO
+
+# 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 constructors will appear in the
+# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.
+# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief
+# member documentation.
+# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting
+# detailed member documentation.
+# The default value is: 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 group names will
+# appear in their defined order.
+# The default value is: NO.
+
+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 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.
+# The default value is: NO.
+
+SORT_BY_SCOPE_NAME = NO
+
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper
+# type resolution of all parameters of a function it will reject a match between
+# the prototype and the implementation of a member function even if there is
+# only one candidate or it is obvious which candidate to choose by doing a
+# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still
+# accept a match between prototype and implementation in such cases.
+# The default value is: NO.
+
+STRICT_PROTO_MATCHING = 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.
+# The default value is: YES.
+
+GENERATE_TODOLIST = NO
+
+# 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.
+# The default value is: YES.
+
+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.
+# The default value is: YES.
+
+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.
+# The default value is: YES.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional documentation
+# sections, marked by \if <section_label> ... \endif and \cond <section_label>
+# ... \endcond blocks.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the
+# initial value of a variable or macro / define can have 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 value of individual variables and macros / defines can be
+# controlled using \showinitializer or \hideinitializer command in the
+# documentation regardless of this setting.
+# Minimum value: 0, maximum value: 10000, default value: 30.
+
+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.
+# The default value is: YES.
+
+SHOW_USED_FILES = YES
+
+# 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 value 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 value 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. For an example see the documentation.
+
+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. To 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.
+#
+# Note that if you run doxygen from a directory containing a file called
+# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
+# tag is left empty.
+
+LAYOUT_FILE =
+
+# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
+# the reference definitions. This must be a list of .bib files. The .bib
+# extension is automatically appended if omitted. This requires the bibtex tool
+# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info.
+# For LaTeX the style of the bibliography can be controlled using
+# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
+# search path. See also \cite for info how to create references.
+
+CITE_BIB_FILES =
+
+#---------------------------------------------------------------------------
+# Configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated to
+# standard output by doxygen. If QUIET is set to YES this implies that the
+# messages are off.
+# The default value is: NO.
+
+QUIET = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES
+# this implies that the warnings are on.
+#
+# Tip: Turn warnings on while writing the documentation.
+# The default value is: YES.
+
+WARNINGS = YES
+
+# If the WARN_IF_UNDOCUMENTED tag 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.
+# The default value is: YES.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# If the WARN_IF_DOC_ERROR tag 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.
+# The default value is: YES.
+
+WARN_IF_DOC_ERROR = YES
+
+# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
+# are documented, but have no documentation for their parameters or return
+# value. If set to NO, doxygen will only warn about wrong or incomplete
+# parameter documentation, but not about the absence of documentation.
+# The default value is: NO.
+
+WARN_NO_PARAMDOC = NO
+
+# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when
+# a warning is encountered.
+# The default value is: NO.
+
+WARN_AS_ERROR = 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)
+# The default value is: $file:$line: $text.
+
+WARN_FORMAT = "$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 standard
+# error (stderr).
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag is 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. See also FILE_PATTERNS and EXTENSION_MAPPING
+# Note: If this tag is empty the current directory is searched.
+
+INPUT = main_page.h ../../src
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
+# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
+# documentation (see: http://www.gnu.org/software/libiconv) for the list of
+# possible encodings.
+# The default value is: UTF-8.
+
+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 patterns (like *.cpp and
+# *.h) to filter out the source-files in the directories.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# read by doxygen.
+#
+# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,
+# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
+# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc,
+# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f, *.for, *.tcl,
+# *.vhd, *.vhdl, *.ucf, *.qsf, *.as and *.js.
+
+FILE_PATTERNS = *.c \
+ *.cc \
+ *.cxx \
+ *.cpp \
+ *.c++ \
+ *.java \
+ *.ii \
+ *.ixx \
+ *.ipp \
+ *.i++ \
+ *.inl \
+ *.h \
+ *.hh \
+ *.hxx \
+ *.hpp \
+ *.h++ \
+ *.idl \
+ *.odl \
+ *.inc \
+ *.m \
+ *.mm \
+ *.dox
+
+# The RECURSIVE tag can be used to specify whether or not subdirectories should
+# be searched for input files as well.
+# The default value is: NO.
+
+RECURSIVE = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should be
+# 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.
+#
+# Note that relative paths are relative to the directory from which doxygen is
+# run.
+
+EXCLUDE = ../../src/include/netif/ppp/polarssl
+
+# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
+# from the input.
+# The default value is: NO.
+
+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
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories use the pattern */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.
+# The default value is: NO.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or directories
+# that contain images that are to be 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.
+#
+# Note that the filter must not add or remove lines; it is applied before the
+# code is scanned, but not when the output code is generated. If lines are added
+# or removed, the anchors will not be placed correctly.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# properly processed by doxygen.
+
+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 information on how
+# filters are used. If the FILTER_PATTERNS tag is empty or if none of the
+# patterns match the file name, INPUT_FILTER is applied.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# properly processed by doxygen.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will also be used to filter the input files that are used for
+# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).
+# The default value is: NO.
+
+FILTER_SOURCE_FILES = NO
+
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and
+# it is also possible to disable source filtering for a specific pattern using
+# *.ext= (so without naming a filter).
+# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.
+
+FILTER_SOURCE_PATTERNS =
+
+# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that
+# is part of the input, its contents will be placed on the main page
+# (index.html). This can be useful if you have a project on for instance GitHub
+# and want to reuse the introduction page also for the doxygen output.
+
+USE_MDFILE_AS_MAINPAGE = main_page.h
+
+#---------------------------------------------------------------------------
+# 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 that
+# also VERBATIM_HEADERS is set to NO.
+# The default value is: NO.
+
+SOURCE_BROWSER = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body of functions,
+# classes and enums directly into the documentation.
+# The default value is: NO.
+
+INLINE_SOURCES = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any
+# special comment blocks from generated source code fragments. Normal C, C++ and
+# Fortran comments will always remain visible.
+# The default value is: YES.
+
+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.
+# The default value is: NO.
+
+REFERENCED_BY_RELATION = NO
+
+# 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.
+# The default value is: NO.
+
+REFERENCES_RELATION = NO
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES 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.
+# The default value is: YES.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the
+# source code will show a tooltip with additional information such as prototype,
+# brief description and links to the definition and documentation. Since this
+# will make the HTML file larger and loading of large files a bit slower, you
+# can opt to disable this feature.
+# The default value is: YES.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+SOURCE_TOOLTIPS = 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.
+#
+# To use it do the following:
+# - Install the latest version of global
+# - Enable SOURCE_BROWSER and USE_HTAGS in the config file
+# - Make sure the INPUT points to the root of the source tree
+# - Run doxygen as normal
+#
+# Doxygen will invoke htags (and that will in turn invoke gtags), so these
+# tools must be available from the command line (i.e. in the search path).
+#
+# The result: instead of the source browser generated by doxygen, the links to
+# source code will now point to the output of htags.
+# The default value is: NO.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set the YES 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.
+# See also: Section \class.
+# The default value is: YES.
+
+VERBATIM_HEADERS = NO
+
+# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the
+# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the
+# cost of reduced performance. This can be particularly helpful with template
+# rich C++ code for which doxygen's built-in parser lacks the necessary type
+# information.
+# Note: The availability of this option depends on whether or not doxygen was
+# generated with the -Duse-libclang=ON option for CMake.
+# The default value is: NO.
+
+CLANG_ASSISTED_PARSING = NO
+
+# If clang assisted parsing is enabled you can provide the compiler with command
+# line options that you would normally use when invoking the compiler. Note that
+# the include paths will already be set by doxygen for the files and directories
+# specified with INPUT and INCLUDE_PATH.
+# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES.
+
+CLANG_OPTIONS =
+
+#---------------------------------------------------------------------------
+# 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.
+# The default value is: YES.
+
+ALPHABETICAL_INDEX = NO
+
+# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in
+# which the alphabetical index list will be split.
+# Minimum value: 1, maximum value: 20, default value: 5.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+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 a prefix (or a list of prefixes) that should be ignored
+# while generating the index headers.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output
+# The default value is: YES.
+
+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.
+# The default directory is: html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+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).
+# The default value is: .html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a user-defined HTML header file for
+# each generated HTML page. If the tag is left blank doxygen will generate a
+# standard header.
+#
+# To get valid HTML the header file that includes any scripts and style sheets
+# that doxygen needs, which is dependent on the configuration options used (e.g.
+# the setting GENERATE_TREEVIEW). It is highly recommended to start with a
+# default header using
+# doxygen -w html new_header.html new_footer.html new_stylesheet.css
+# YourConfigFile
+# and then modify the file new_header.html. See also section "Doxygen usage"
+# for information on how to generate the default header that doxygen normally
+# uses.
+# Note: The header is subject to change so you typically have to regenerate the
+# default header when upgrading to a newer version of doxygen. For a description
+# of the possible markers and block names see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
+# generated HTML page. If the tag is left blank doxygen will generate a standard
+# footer. See HTML_HEADER for more information on how to generate a default
+# footer and what special commands can be used inside the footer. See also
+# section "Doxygen usage" for information on how to generate the default footer
+# that doxygen normally uses.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+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 left blank doxygen will generate a default style sheet.
+# See also section "Doxygen usage" for information on how to generate the style
+# sheet that doxygen normally uses.
+# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as
+# it is more robust and this tag (HTML_STYLESHEET) will in the future become
+# obsolete.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_STYLESHEET =
+
+# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined
+# cascading style sheets that are included after the standard style sheets
+# created by doxygen. Using this option one can overrule certain style aspects.
+# This is preferred over using HTML_STYLESHEET since it does not replace the
+# standard style sheet and is therefore more robust against future updates.
+# Doxygen will copy the style sheet files to the output directory.
+# Note: The order of the extra style sheet files is of importance (e.g. the last
+# style sheet in the list overrules the setting of the previous ones in the
+# list). For an example see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_STYLESHEET =
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that the
+# files will be copied as-is; there are no commands or markers available.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_FILES =
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
+# will adjust the colors in the style sheet 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.
+# Minimum value: 0, maximum value: 359, default value: 220.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+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.
+# Minimum value: 0, maximum value: 255, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+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.
+# Minimum value: 40, maximum value: 240, default value: 80.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+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 YES can help to show when doxygen was last run and thus if the
+# documentation is up to date.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_TIMESTAMP = NO
+
+# 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.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_DYNAMIC_SECTIONS = NO
+
+# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
+# shown in the various tree structured indices initially; the user can expand
+# and collapse entries dynamically later on. Doxygen will expand the tree to
+# such a level that at most the specified number of entries are visible (unless
+# a fully collapsed tree already exceeds this amount). So setting the number of
+# entries 1 will produce a full collapsed tree by default. 0 is a special value
+# representing an infinite number of entries and will result in a full expanded
+# tree by default.
+# Minimum value: 0, maximum value: 9999, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_INDEX_NUM_ENTRIES = 100
+
+# 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 (see: http://developer.apple.com/tools/xcode/), 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.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_DOCSET = NO
+
+# This tag determines the name of the docset 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.
+# The default value is: Doxygen generated docs.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_FEEDNAME = "Doxygen generated docs"
+
+# 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.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_BUNDLE_ID = org.doxygen.Project
+
+# The DOCSET_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.
+# The default value is: org.doxygen.Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_ID = org.doxygen.Publisher
+
+# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.
+# The default value is: Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_NAME = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
+# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
+# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
+# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on
+# Windows.
+#
+# The HTML Help Workshop contains a compiler that can convert all HTML output
+# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
+# files are now used as the Windows 98 help format, and will replace the old
+# Windows help format (.hlp) on all Windows platforms in the future. Compressed
+# HTML files also contain an index, a table of contents, and you can search for
+# words in the documentation. The HTML workshop also contains a viewer for
+# compressed HTML files.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_HTMLHELP = NO
+
+# 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.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_FILE = lwip.chm
+
+# 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.
+# The file has to be specified with full path.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+HHC_LOCATION =
+
+# 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).
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+GENERATE_CHI = NO
+
+# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc)
+# and project file content.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_INDEX_ENCODING =
+
+# 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. Furthermore it
+# enables the Previous and Next buttons.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members to
+# the table of contents of the HTML help documentation and to the tree view.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+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.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+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.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QCH_FILE =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
+# Project output. For more information please see Qt Help Project / Namespace
+# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace).
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+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 Qt Help Project / Virtual
+# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual-
+# folders).
+# The default value is: doc.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_VIRTUAL_FOLDER = doc
+
+# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
+# filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_NAME =
+
+# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_ATTRS =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's filter section matches. Qt Help Project / Filter Attributes (see:
+# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_SECT_FILTER_ATTRS =
+
+# 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.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHG_LOCATION =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be
+# generated, together with the HTML files, they 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.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+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. Each documentation set should have its own identifier.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.
+
+ECLIPSE_DOC_ID = org.doxygen.Project
+
+# If you want full control over the layout of the generated HTML pages it might
+# be necessary to disable the index and replace it with your own. The
+# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top
+# of each HTML page. A value of NO enables the index and the value YES disables
+# it. Since the tabs in the index contain the same information as the navigation
+# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+DISABLE_INDEX = NO
+
+# 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. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can
+# further fine-tune the look of the index. As an example, the default style
+# sheet generated by doxygen has an example that shows how to put an image at
+# the root of the tree instead of the PROJECT_NAME. Since the tree basically has
+# the same information as the tab index, you could consider setting
+# DISABLE_INDEX to YES when enabling this option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_TREEVIEW = YES
+
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
+# doxygen will group on one line in the generated HTML documentation.
+#
+# Note that a value of 0 will completely suppress the enum values from appearing
+# in the overview section.
+# Minimum value: 0, maximum value: 20, default value: 4.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+ENUM_VALUES_PER_LINE = 4
+
+# 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.
+# Minimum value: 0, maximum value: 1500, default value: 250.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+TREEVIEW_WIDTH = 250
+
+# If 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.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+EXT_LINKS_IN_WINDOW = NO
+
+# Use this tag to change the font size of LaTeX formulas included as images in
+# the HTML documentation. 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.
+# Minimum value: 8, maximum value: 50, default value: 10.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+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 directory before the changes have effect.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+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 pre-rendered 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 MathJax is enabled you can set the default output format to be used for
+# the MathJax output. See the MathJax site (see:
+# http://docs.mathjax.org/en/latest/output.html) for more details.
+# Possible values are: HTML-CSS (which is slower, but has the best
+# compatibility), NativeMML (i.e. MathML) and SVG.
+# The default value is: HTML-CSS.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_FORMAT = HTML-CSS
+
+# When MathJax is enabled you need to specify the location relative to the HTML
+# output directory using the MATHJAX_RELPATH option. The destination directory
+# should contain the MathJax.js script. For instance, if the mathjax directory
+# is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
+# Content Delivery Network so you can quickly see the result without installing
+# MathJax. However, it is strongly recommended to install a local copy of
+# MathJax from http://www.mathjax.org before deployment.
+# The default value is: http://cdn.mathjax.org/mathjax/latest.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest
+
+# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
+# extension names that should be enabled during MathJax rendering. For example
+# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_EXTENSIONS =
+
+# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
+# of code that will be used on startup of the MathJax code. See the MathJax site
+# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an
+# example see the documentation.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_CODEFILE =
+
+# 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. It is possible to
+# search using the keyboard; to jump to the search box use <access key> + S
+# (what the <access key> is depends on the OS and browser, but it is typically
+# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down
+# key> to jump into the search results window, the results can be navigated
+# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel
+# the search. The filter options can be selected when the cursor is inside the
+# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>
+# to select a filter and <Enter> or <escape> to activate or cancel the filter
+# option.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+SEARCHENGINE = YES
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a web server instead of a web client using Javascript. There
+# are two flavors of web server based searching depending on the EXTERNAL_SEARCH
+# setting. When disabled, doxygen will generate a PHP script for searching and
+# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing
+# and searching needs to be provided by external tools. See the section
+# "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SERVER_BASED_SEARCH = NO
+
+# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP
+# script for searching. Instead the search results are written to an XML file
+# which needs to be processed by an external indexer. Doxygen will invoke an
+# external search engine pointed to by the SEARCHENGINE_URL option to obtain the
+# search results.
+#
+# Doxygen ships with an example indexer (doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/).
+#
+# See the section "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH = NO
+
+# The SEARCHENGINE_URL should point to a search engine hosted by a web server
+# which will return the search results when EXTERNAL_SEARCH is enabled.
+#
+# Doxygen ships with an example indexer (doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/). See the section "External Indexing and
+# Searching" for details.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHENGINE_URL =
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
+# search data is written to a file for indexing by an external tool. With the
+# SEARCHDATA_FILE tag the name of this file can be specified.
+# The default file is: searchdata.xml.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHDATA_FILE = searchdata.xml
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the
+# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is
+# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple
+# projects and redirect the results back to the right project.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH_ID =
+
+# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
+# projects other than the one defined by this configuration file, but that are
+# all added to the same external search index. Each project needs to have a
+# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of
+# to a relative location where the documentation can be found. The format is:
+# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTRA_SEARCH_MAPPINGS =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output.
+# The default value is: YES.
+
+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.
+# The default directory is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked.
+#
+# 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.
+# The default file is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
+# index for LaTeX.
+# The default file is: makeindex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+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.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+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 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x
+# 14 inches) and executive (7.25 x 10.5 inches).
+# The default value is: a4.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PAPER_TYPE = a4wide
+
+# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
+# that should be included in the LaTeX output. The package can be specified just
+# by its name or with the correct syntax as to be used with the LaTeX
+# \usepackage command. To get the times font for instance you can specify :
+# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times}
+# To use the option intlimits with the amsmath package you can specify:
+# EXTRA_PACKAGES=[intlimits]{amsmath}
+# If left blank no extra packages will be included.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+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. See
+# section "Doxygen usage" for information on how to let doxygen write the
+# default header to a separate file.
+#
+# Note: Only use a user-defined header if you know what you are doing! The
+# following commands have a special meaning inside the header: $title,
+# $datetime, $date, $doxygenversion, $projectname, $projectnumber,
+# $projectbrief, $projectlogo. Doxygen will replace $title with the empty
+# string, for the replacement values of the other commands the user is referred
+# to HTML_HEADER.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HEADER =
+
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the
+# generated LaTeX document. The footer should contain everything after the last
+# chapter. If it is left blank doxygen will generate a standard footer. See
+# LATEX_HEADER for more information on how to generate a default footer and what
+# special commands can be used inside the footer.
+#
+# Note: Only use a user-defined footer if you know what you are doing!
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_FOOTER =
+
+# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined
+# LaTeX style sheets that are included after the standard style sheets created
+# by doxygen. Using this option one can overrule certain style aspects. Doxygen
+# will copy the style sheet files to the output directory.
+# Note: The order of the extra style sheet files is of importance (e.g. the last
+# style sheet in the list overrules the setting of the previous ones in the
+# list).
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_STYLESHEET =
+
+# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the LATEX_OUTPUT output
+# directory. Note that the files will be copied as-is; there are no commands or
+# markers available.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_FILES =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is
+# prepared for conversion to PDF (using ps2pdf or pdflatex). 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.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PDF_HYPERLINKS = NO
+
+# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
+# the PDF file directly from the LaTeX files. Set this option to YES, to get a
+# higher quality PDF documentation.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+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.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BATCHMODE = NO
+
+# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the
+# index chapters (such as File Index, Compound Index, etc.) in the output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HIDE_INDICES = NO
+
+# If the LATEX_SOURCE_CODE tag 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.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_SOURCE_CODE = NO
+
+# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
+# bibliography, e.g. plainnat, or ieeetr. See
+# http://en.wikipedia.org/wiki/BibTeX and \cite for more info.
+# The default value is: plain.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BIB_STYLE = plain
+
+# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated
+# 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.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_TIMESTAMP = 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 too pretty with other RTF
+# readers/editors.
+# The default value is: NO.
+
+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.
+# The default directory is: rtf.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+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.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+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 some other Word compatible readers that support those
+# fields.
+#
+# Note: WordPad (write) and others do not support links.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_HYPERLINKS = NO
+
+# 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.
+#
+# See also section "Doxygen usage" for information on how to generate the
+# default style sheet that doxygen normally uses.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an RTF document. Syntax is
+# similar to doxygen's config file. A template extensions file can be generated
+# using doxygen -e rtf extensionFile.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_EXTENSIONS_FILE =
+
+# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code
+# with syntax highlighting in the RTF output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_SOURCE_CODE = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for
+# classes and files.
+# The default value is: NO.
+
+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. A directory man3 will be created inside the directory specified by
+# MAN_OUTPUT.
+# The default directory is: man.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to the generated
+# man pages. In case the manual section does not start with a number, the number
+# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is
+# optional.
+# The default value is: .3.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_EXTENSION = .3
+
+# The MAN_SUBDIR tag determines the name of the directory created within
+# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by
+# MAN_EXTENSION with the initial . removed.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_SUBDIR =
+
+# 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 value is: NO.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+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.
+# The default value is: NO.
+
+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.
+# The default directory is: xml.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_OUTPUT = xml
+
+# 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.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_PROGRAMLISTING = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to the DOCBOOK output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files
+# that can be used to generate PDF.
+# The default value is: NO.
+
+GENERATE_DOCBOOK = NO
+
+# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in
+# front of it.
+# The default directory is: docbook.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_OUTPUT = docbook
+
+# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the
+# program listings (including syntax highlighting and cross-referencing
+# information) to the DOCBOOK output. Note that enabling this will significantly
+# increase the size of the DOCBOOK output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_PROGRAMLISTING = NO
+
+#---------------------------------------------------------------------------
+# Configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an
+# AutoGen Definitions (see http://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.
+# The default value is: NO.
+
+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.
+# The default value is: NO.
+
+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.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+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.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+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.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all
+# C-preprocessor directives found in the sources and include files.
+# The default value is: YES.
+
+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, only conditional compilation will be
+# performed. Macro expansion can be done in a controlled way by setting
+# EXPAND_ONLY_PREDEF to YES.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set 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.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_ONLY_PREDEF = NO
+
+# If the SEARCH_INCLUDES tag is set to YES, the include files in the
+# INCLUDE_PATH will be searched if a #include is found.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+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.
+# This tag requires that the tag SEARCH_INCLUDES is set to YES.
+
+INCLUDE_PATH = ../../src/include
+
+# 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.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+INCLUDE_FILE_PATTERNS = *.h
+
+# 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 e.g.
+# 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.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+PREDEFINED = __DOXYGEN__=1 \
+ NO_SYS=0 \
+ SYS_LIGHTWEIGHT_PROT=1 \
+ LWIP_TCPIP_CORE_LOCKING=1 \
+ LWIP_IPV4=1 \
+ LWIP_IPV6=1 \
+ LWIP_ICMP=1 \
+ LWIP_RAW=1 \
+ LWIP_DHCP=1 \
+ LWIP_UDPLITE=1 \
+ LWIP_UDP=1 \
+ LWIP_IGMP=1 \
+ LWIP_TCP=1 \
+ TCP_LISTEN_BACKLOG=1 \
+ LWIP_SNMP=1 \
+ SNMP_USE_NETCONN=1 \
+ SNMP_USE_RAW=1 \
+ MIB2_STATS=1 \
+ LWIP_MDNS_RESPONDER=1 \
+ MEMP_OVERFLOW_CHECK=0 \
+ MEMP_SANITY_CHECK=1 \
+ LWIP_ARP=1 \
+ LWIP_HAVE_LOOPIF=1 \
+ LWIP_NETIF_HOSTNAME=1 \
+ LWIP_NETIF_API=1 \
+ LWIP_NETIF_CALLBACK=1 \
+ LWIP_NETIF_STATUS_CALLBACK=1 \
+ LWIP_NETIF_REMOVE_CALLBACK=1 \
+ LWIP_NETIF_LINK_CALLBACK=1 \
+ LWIP_NUM_NETIF_CLIENT_DATA=1 \
+ ENABLE_LOOPBACK=1 \
+ LWIP_AUTOIP=1 \
+ ARP_QUEUEING=1 \
+ LWIP_STATS=1 \
+ MEM_USE_POOLS=0 \
+ LWIP_DNS=1 \
+ LWIP_SOCKET=1 \
+ LWIP_NETCONN=1 \
+ IP_SOF_BROADCAST=1 \
+ IP_SOF_BROADCAST_RECV=1 \
+ LWIP_NETIF_API=1 \
+ LWIP_SO_SNDTIMEO=1 \
+ LWIP_SO_RCVBUF=1 \
+ LWIP_SO_LINGER=1 \
+ SO_REUSE=1 \
+ SO_REUSE_RXTOALL=1 \
+ LWIP_HAVE_SLIPIF=1 \
+ LWIP_6LOWPAN=1
+
+# 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 that overrules the
+# definition found in the source code.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will
+# remove all references to 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.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SKIP_FUNCTION_MACROS = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES tag can be used to specify one or more tag files. For each tag
+# file the location of the external documentation should be added. 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. See the
+# section "Linking to external documentation" for more information about the use
+# of tag files.
+# Note: 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. See section "Linking to
+# external documentation" for more information about the usage of tag files.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES, all external class will be listed in
+# the class index. If set to NO, only the inherited external classes will be
+# listed.
+# The default value is: NO.
+
+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.
+# The default value is: YES.
+
+EXTERNAL_GROUPS = YES
+
+# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in
+# the related pages index. If set to NO, only the current project's pages will
+# be listed.
+# The default value is: YES.
+
+EXTERNAL_PAGES = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of 'which perl').
+# The default file (with absolute path) is: /usr/bin/perl.
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram
+# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to
+# NO turns the diagrams off. Note that this option also works with HAVE_DOT
+# disabled, but it is recommended to install and use dot, since it yields more
+# powerful graphs.
+# The default value is: YES.
+
+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 =
+
+# You can include diagrams made with dia in doxygen documentation. Doxygen will
+# then run dia to produce the diagram and insert it in the documentation. The
+# DIA_PATH tag allows you to specify the directory where the dia binary resides.
+# If left empty dia is assumed to be found in the default search path.
+
+DIA_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.
+# The default value is: YES.
+
+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 (see:
+# http://www.graphviz.org/), 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 value is: YES.
+
+HAVE_DOT = NO
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
+# to run in parallel. When set to 0 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.
+# Minimum value: 0, maximum value: 32, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_NUM_THREADS = 0
+
+# When you want a differently looking font in the dot files that doxygen
+# generates you can specify the font name using DOT_FONTNAME. You 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.
+# The default value is: Helvetica.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTNAME = Helvetica
+
+# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
+# dot graphs.
+# Minimum value: 4, maximum value: 24, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTSIZE = 10
+
+# By default doxygen will tell dot to use the default font as specified with
+# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
+# the path where dot can find it using this tag.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTPATH =
+
+# If the CLASS_GRAPH tag is 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 CLASS_DIAGRAMS tag to NO.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH tag is 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.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+COLLABORATION_GRAPH = YES
+
+# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
+# groups, showing the direct groups dependencies.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+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.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LOOK = NO
+
+# If the UML_LOOK tag is enabled, the fields and methods are shown inside the
+# class node. If there are many fields or methods and many nodes the graph may
+# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the
+# number of items for each type to make the size more manageable. Set this to 0
+# for no limit. Note that the threshold may be exceeded by 50% before the limit
+# is enforced. So when you set the threshold to 10, up to 15 fields may appear,
+# but if the number exceeds 15, the total amount of fields shown is limited to
+# 10.
+# Minimum value: 0, maximum value: 100, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LIMIT_NUM_FIELDS = 10
+
+# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and
+# collaboration graphs will show the relations between templates and their
+# instances.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+TEMPLATE_RELATIONS = NO
+
+# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES 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.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDE_GRAPH = YES
+
+# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES 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.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the CALL_GRAPH tag is 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. Disabling a call graph can be
+# accomplished by means of the command \hidecallgraph.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALL_GRAPH = NO
+
+# If the CALLER_GRAPH tag is 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. Disabling a caller graph can be
+# accomplished by means of the command \hidecallergraph.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALLER_GRAPH = NO
+
+# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical
+# hierarchy of all classes instead of a textual one.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GRAPHICAL_HIERARCHY = YES
+
+# If the DIRECTORY_GRAPH tag is 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.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DIRECTORY_GRAPH = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. For an explanation of the image formats see the section
+# output formats in the documentation of the dot tool (Graphviz (see:
+# http://www.graphviz.org/)).
+# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
+# to make the SVG files visible in IE 9+ (other browsers do not have this
+# requirement).
+# Possible values are: png, png:cairo, png:cairo:cairo, png:cairo:gd, png:gd,
+# png:gd:gd, jpg, jpg:cairo, jpg:cairo:gd, jpg:gd, jpg:gd:gd, gif, gif:cairo,
+# gif:cairo:gd, gif:gd, gif:gd:gd, svg, png:gd, png:gd:gd, png:cairo,
+# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and
+# png:gdiplus:gdiplus.
+# The default value is: png.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_IMAGE_FORMAT = png
+
+# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
+# enable generation of interactive SVG images that allow zooming and panning.
+#
+# Note that this requires a modern browser other than Internet Explorer. Tested
+# and working are Firefox, Chrome, Safari, and Opera.
+# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make
+# the SVG files visible. Older versions of IE do not have SVG support.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INTERACTIVE_SVG = NO
+
+# The DOT_PATH tag 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.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+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).
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOTFILE_DIRS =
+
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the \mscfile
+# command).
+
+MSCFILE_DIRS =
+
+# The DIAFILE_DIRS tag can be used to specify one or more directories that
+# contain dia files that are included in the documentation (see the \diafile
+# command).
+
+DIAFILE_DIRS =
+
+# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the
+# path where java can find the plantuml.jar file. If left blank, it is assumed
+# PlantUML is not used or called during a preprocessing step. Doxygen will
+# generate a warning when it encounters a \startuml command in this case and
+# will not generate output for the diagram.
+
+PLANTUML_JAR_PATH =
+
+# When using plantuml, the specified paths are searched for files specified by
+# the !include statement in a plantuml block.
+
+PLANTUML_INCLUDE_PATH =
+
+# 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.
+# Minimum value: 0, maximum value: 10000, default value: 50.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+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.
+# Minimum value: 0, maximum value: 1000, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+MAX_DOT_GRAPH_DEPTH = 1000
+
+# 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).
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_TRANSPARENT = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES to 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.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_MULTI_TARGETS = NO
+
+# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page
+# explaining the meaning of the various boxes and arrows in the dot generated
+# graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate dot
+# files that are used to generate the various graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_CLEANUP = YES
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/doc/doxygen/main_page.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/doc/doxygen/main_page.h
new file mode 100644
index 0000000..d35d16e
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/doc/doxygen/main_page.h
@@ -0,0 +1,132 @@
+/**
+ * @defgroup lwip lwIP
+ *
+ * @defgroup infrastructure Infrastructure
+ *
+ * @defgroup callbackstyle_api Callback-style APIs
+ * Non thread-safe APIs, callback style for maximum performance and minimum
+ * memory footprint.
+ *
+ * @defgroup sequential_api Sequential-style APIs
+ * Sequential-style APIs, blocking functions. More overhead, but can be called
+ * from any thread except TCPIP thread.
+ *
+ * @defgroup addons Addons
+ *
+ * @defgroup apps Applications
+ */
+
+/**
+ * @mainpage Overview
+ * @verbinclude "README"
+ */
+
+/**
+ * @page upgrading Upgrading
+ * @verbinclude "UPGRADING"
+ */
+
+/**
+ * @page changelog Changelog
+ * @verbinclude "CHANGELOG"
+ */
+
+/**
+ * @page contrib How to contribute to lwIP
+ * @verbinclude "contrib.txt"
+ */
+
+/**
+ * @page pitfalls Common pitfalls
+ *
+ * Multiple Execution Contexts in lwIP code
+ * ========================================
+ *
+ * The most common source of lwIP problems is to have multiple execution contexts
+ * inside the lwIP code.
+ *
+ * lwIP can be used in two basic modes: @ref lwip_nosys (no OS/RTOS
+ * running on target system) or @ref lwip_os (there is an OS running
+ * on the target system).
+ *
+ * Mainloop Mode
+ * -------------
+ * In mainloop mode, only @ref callbackstyle_api can be used.
+ * The user has two possibilities to ensure there is only one
+ * exection context at a time in lwIP:
+ *
+ * 1) Deliver RX ethernet packets directly in interrupt context to lwIP
+ * by calling netif->input directly in interrupt. This implies all lwIP
+ * callback functions are called in IRQ context, which may cause further
+ * problems in application code: IRQ is blocked for a long time, multiple
+ * execution contexts in application code etc. When the application wants
+ * to call lwIP, it only needs to disable interrupts during the call.
+ * If timers are involved, even more locking code is needed to lock out
+ * timer IRQ and ethernet IRQ from each other, assuming these may be nested.
+ *
+ * 2) Run lwIP in a mainloop. There is example code here: @ref lwip_nosys.
+ * lwIP is _ONLY_ called from mainloop callstacks here. The ethernet IRQ
+ * has to put received telegrams into a queue which is polled in the
+ * mainloop. Ensure lwIP is _NEVER_ called from an interrupt, e.g.
+ * some SPI IRQ wants to forward data to udp_send() or tcp_write()!
+ *
+ * OS Mode
+ * -------
+ * In OS mode, @ref callbackstyle_api AND @ref sequential_api can be used.
+ * @ref sequential_api are designed to be called from threads other than
+ * the TCPIP thread, so there is nothing to consider here.
+ * But @ref callbackstyle_api functions must _ONLY_ be called from
+ * TCPIP thread. It is a common error to call these from other threads
+ * or from IRQ contexts. ​Ethernet RX needs to deliver incoming packets
+ * in the correct way by sending a message to TCPIP thread, this is
+ * implemented in tcpip_input().​​
+ * Again, ensure lwIP is _NEVER_ called from an interrupt, e.g.
+ * some SPI IRQ wants to forward data to udp_send() or tcp_write()!
+ *
+ * 1) tcpip_callback() can be used get called back from TCPIP thread,
+ * it is safe to call any @ref callbackstyle_api from there.
+ *
+ * 2) Use @ref LWIP_TCPIP_CORE_LOCKING. All @ref callbackstyle_api
+ * functions can be called when lwIP core lock is aquired, see
+ * @ref LOCK_TCPIP_CORE() and @ref UNLOCK_TCPIP_CORE().
+ * These macros cannot be used in an interrupt context!
+ * Note the OS must correctly handle priority inversion for this.
+ */
+
+/**
+ * @page bugs Reporting bugs
+ * Please report bugs in the lwIP bug tracker at savannah.\n
+ * BEFORE submitting, please check if the bug has already been reported!\n
+ * https://savannah.nongnu.org/bugs/?group=lwip
+ */
+
+/**
+ * @defgroup lwip_nosys Mainloop mode ("NO_SYS")
+ * @ingroup lwip
+ * Use this mode if you do not run an OS on your system. \#define NO_SYS to 1.
+ * Feed incoming packets to netif->input(pbuf, netif) function from mainloop,
+ * *not* *from* *interrupt* *context*. You can allocate a @ref pbuf in interrupt
+ * context and put them into a queue which is processed from mainloop.\n
+ * Call sys_check_timeouts() periodically in the mainloop.\n
+ * Porting: implement all functions in @ref sys_time, @ref sys_prot and
+ * @ref compiler_abstraction.\n
+ * You can only use @ref callbackstyle_api in this mode.\n
+ * Sample code:\n
+ * @include NO_SYS_SampleCode.c
+ */
+
+/**
+ * @defgroup lwip_os OS mode (TCPIP thread)
+ * @ingroup lwip
+ * Use this mode if you run an OS on your system. It is recommended to
+ * use an RTOS that correctly handles priority inversion and
+ * to use @ref LWIP_TCPIP_CORE_LOCKING.\n
+ * Porting: implement all functions in @ref sys_layer.\n
+ * You can use @ref callbackstyle_api together with @ref tcpip_callback,
+ * and all @ref sequential_api.
+ */
+
+/**
+ * @page raw_api lwIP API
+ * @verbinclude "rawapi.txt"
+ */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/doc/doxygen/output/index.html b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/doc/doxygen/output/index.html
new file mode 100644
index 0000000..a52e09f
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/doc/doxygen/output/index.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title>Redirection</title>
+ <meta http-equiv="refresh" content="0; url=html/index.html" />
+</head>
+<body>
+ <a href="html/index.html">index.html</a>
+</body>
+</html>
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/doc/mdns.txt b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/doc/mdns.txt
new file mode 100644
index 0000000..c322843
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/doc/mdns.txt
@@ -0,0 +1,113 @@
+Multicast DNS for lwIP
+
+Author: Erik Ekman
+
+
+Note! The MDNS responder does not have all features required by the standards.
+See notes in src/apps/mdns/mdns.c for what is left. It is however usable in normal
+cases - but watch out if many devices on the same network try to use the same
+host/service instance names.
+
+
+How to enable:
+==============
+
+MDNS support does not depend on DNS.
+MDNS supports using IPv4 only, v6 only, or v4+v6.
+
+To enable MDNS responder, set
+ LWIP_MDNS_RESPONDER = 1
+in lwipopts.h and add src/apps/mdns/mdns.c to your list of files to build.
+
+The max number of services supported per netif is defined by MDNS_MAX_SERVICES,
+default is 1.
+
+Increase MEMP_NUM_UDP_PCB by 1. MDNS needs one PCB.
+Increase LWIP_NUM_NETIF_CLIENT_DATA by 1 (MDNS needs one entry on netif).
+
+MDNS with IPv4 requires LWIP_IGMP = 1, and preferably LWIP_AUTOIP = 1.
+MDNS with IPv6 requires LWIP_IPV6_MLD = 1, and that a link-local address is
+generated.
+
+The MDNS code puts its structs on the stack where suitable to reduce dynamic
+memory allocation. It may use up to 1kB of stack.
+
+MDNS needs a strncasecmp() implementation. If you have one, define
+LWIP_MDNS_STRNCASECMP to it. Otherwise the code will provide an implementation
+for you.
+
+
+How to use:
+===========
+
+Call mdns_resp_init() during system initialization.
+This opens UDP sockets on port 5353 for IPv4 and IPv6.
+
+
+To start responding on a netif, run
+ mdns_resp_add_netif(struct netif *netif, char *hostname, u32_t dns_ttl)
+
+The hostname will be copied. If this returns successfully, the netif will join
+the multicast groups and any MDNS/legacy DNS requests sent unicast or multicast
+to port 5353 will be handled:
+- <hostname>.local type A, AAAA or ANY returns relevant IP addresses
+- Reverse lookups (PTR in-addr.arpa, ip6.arpa) of netif addresses
+ returns <hostname>.local
+Answers will use the supplied TTL (in seconds)
+MDNS allows UTF-8 names, but it is recommended to stay within ASCII,
+since the default case-insensitive comparison assumes this.
+
+It is recommended to call this function after an IPv4 address has been set,
+since there is currently no check if the v4 address is valid.
+
+Call mdns_resp_netif_settings_changed() every time the IP address
+on the netif has changed.
+
+To stop responding on a netif, run
+ mdns_resp_remove_netif(struct netif *netif)
+
+
+Adding services:
+================
+
+The netif first needs to be registered. Then run
+ mdns_resp_add_service(struct netif *netif, char *name, char *service,
+ u16_t proto, u16_t port, u32_t dns_ttl,
+ service_get_txt_fn_t txt_fn, void *txt_userdata);
+
+The name and service pointers will be copied. Name refers to the name of the
+service instance, and service is the type of service, like _http
+proto can be DNSSD_PROTO_UDP or DNSSD_PROTO_TCP which represent _udp and _tcp.
+If this call returns successfully, the following queries will be answered:
+- _services._dns-sd._udp.local type PTR returns <service>.<proto>.local
+- <service>.<proto>.local type PTR returns <name>.<service>.<proto>.local
+- <name>.<service>.<proto>.local type SRV returns hostname and port of service
+- <name>.<service>.<proto>.local type TXT builds text strings by calling txt_fn
+ with the supplied userdata. The callback adds strings to the reply by calling
+ mdns_resp_add_service_txtitem(struct mdns_service *service, char *txt,
+ int txt_len). Example callback method:
+
+ static void srv_txt(struct mdns_service *service, void *txt_userdata)
+ {
+ res = mdns_resp_add_service_txtitem(service, "path=/", 6);
+ LWIP_ERROR("mdns add service txt failed\n", (res == ERR_OK), return);
+ }
+
+ Since a hostname struct is used for TXT storage each single item can be max
+ 63 bytes long, and the total max length (including length bytes for each
+ item) is 255 bytes.
+
+If your device runs a webserver on port 80, an example call might be:
+
+ mdns_resp_add_service(netif, "myweb", "_http"
+ DNSSD_PROTO_TCP, 80, 3600, srv_txt, NULL);
+
+which will publish myweb._http._tcp.local for any hosts looking for web servers,
+and point them to <hostname>.local:80
+
+Relevant information will be sent as additional records to reduce number of
+requests required from a client.
+
+Removing services is currently not supported. Services are removed when the
+netif is removed.
+
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/doc/mqtt_client.txt b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/doc/mqtt_client.txt
new file mode 100644
index 0000000..3e67def
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/doc/mqtt_client.txt
@@ -0,0 +1,162 @@
+MQTT client for lwIP
+
+Author: Erik Andersson
+
+Details of the MQTT protocol can be found at:
+http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html
+
+-----------------------------------------------------------------
+1. Initial steps, reserve memory and make connection to server:
+
+1.1: Provide storage
+
+Static allocation:
+ mqtt_client_t static_client;
+ example_do_connect(&static_client);
+
+Dynamic allocation:
+ mqtt_client_t *client = mqtt_client_new();
+ if(client != NULL) {
+ example_do_connect(&client);
+ }
+
+1.2: Establish Connection with server
+
+void example_do_connect(mqtt_client_t *client)
+{
+ struct mqtt_connect_client_info_t ci;
+ err_t err;
+
+ /* Setup an empty client info structure */
+ memset(&ci, 0, sizeof(ci));
+
+ /* Minimal amount of information required is client identifier, so set it here */
+ ci.client_id = "lwip_test";
+
+ /* Initiate client and connect to server, if this fails immediately an error code is returned
+ otherwise mqtt_connection_cb will be called with connection result after attempting
+ to establish a connection with the server.
+ For now MQTT version 3.1.1 is always used */
+
+ err = mqtt_client_connect(client, ip_addr, MQTT_PORT, mqtt_connection_cb, 0, &ci);
+
+ /* For now just print the result code if something goes wrong
+ if(err != ERR_OK) {
+ printf("mqtt_connect return %d\n", err);
+ }
+}
+
+Connection to server can also be probed by calling mqtt_client_is_connected(client)
+
+-----------------------------------------------------------------
+2. Implementing the connection status callback
+
+
+static void mqtt_connection_cb(mqtt_client_t *client, void *arg, mqtt_connection_status_t status)
+{
+ err_t err;
+ if(status == MQTT_CONNECT_ACCEPTED) {
+ printf("mqtt_connection_cb: Successfully connected\n");
+
+ /* Setup callback for incoming publish requests */
+ mqtt_set_inpub_callback(client, mqtt_incoming_publish_cb, mqtt_incoming_data_cb, arg);
+
+ /* Subscribe to a topic named "subtopic" with QoS level 1, call mqtt_sub_request_cb with result */
+ err = mqtt_subscribe(client, "subtopic", 1, mqtt_sub_request_cb, arg);
+
+ if(err != ERR_OK) {
+ printf("mqtt_subscribe return: %d\n", err);
+ }
+ } else {
+ printf("mqtt_connection_cb: Disconnected, reason: %d\n", status);
+
+ /* Its more nice to be connected, so try to reconnect */
+ example_do_connect(client);
+ }
+}
+
+static void mqtt_sub_request_cb(void *arg, err_t result)
+{
+ /* Just print the result code here for simplicity,
+ normal behaviour would be to take some action if subscribe fails like
+ notifying user, retry subscribe or disconnect from server */
+ printf("Subscribe result: %d\n", result);
+}
+
+-----------------------------------------------------------------
+3. Implementing callbacks for incoming publish and data
+
+/* The idea is to demultiplex topic and create some reference to be used in data callbacks
+ Example here uses a global variable, better would be to use a member in arg
+ If RAM and CPU budget allows it, the easiest implementation might be to just take a copy of
+ the topic string and use it in mqtt_incoming_data_cb
+*/
+static int inpub_id;
+static void mqtt_incoming_publish_cb(void *arg, const char *topic, u32_t tot_len)
+{
+ printf("Incoming publish at topic %s with total length %u\n", topic, (unsigned int)tot_len);
+
+ /* Decode topic string into a user defined reference */
+ if(strcmp(topic, "print_payload") == 0) {
+ inpub_id = 0;
+ } else if(topic[0] == 'A') {
+ /* All topics starting with 'A' might be handled at the same way */
+ inpub_id = 1;
+ } else {
+ /* For all other topics */
+ inpub_id = 2;
+ }
+}
+
+static void mqtt_incoming_data_cb(void *arg, const u8_t *data, u16_t len, u8_t flags)
+{
+ printf("Incoming publish payload with length %d, flags %u\n", len, (unsigned int)flags);
+
+ if(flags & MQTT_DATA_FLAG_LAST) {
+ /* Last fragment of payload received (or whole part if payload fits receive buffer
+ See MQTT_VAR_HEADER_BUFFER_LEN) */
+
+ /* Call function or do action depending on reference, in this case inpub_id */
+ if(inpub_id == 0) {
+ /* Don't trust the publisher, check zero termination */
+ if(data[len-1] == 0) {
+ printf("mqtt_incoming_data_cb: %s\n", (const char *)data);
+ }
+ } else if(inpub_id == 1) {
+ /* Call an 'A' function... */
+ } else {
+ printf("mqtt_incoming_data_cb: Ignoring payload...\n");
+ }
+ } else {
+ /* Handle fragmented payload, store in buffer, write to file or whatever */
+ }
+}
+
+-----------------------------------------------------------------
+4. Using outgoing publish
+
+
+void example_publish(mqtt_client_t *client, void *arg)
+{
+ const char *pub_payload= "PubSubHubLubJub";
+ err_t err;
+ u8_t qos = 2; /* 0 1 or 2, see MQTT specification */
+ u8_t retain = 0; /* No don't retain such crappy payload... */
+ err = mqtt_publish(client, "pub_topic", pub_payload, strlen(pub_payload), qos, retain, mqtt_pub_request_cb, arg);
+ if(err != ERR_OK) {
+ printf("Publish err: %d\n", err);
+ }
+}
+
+/* Called when publish is complete either with sucess or failure */
+static void mqtt_pub_request_cb(void *arg, err_t result)
+{
+ if(result != ERR_OK) {
+ printf("Publish result: %d\n", result);
+ }
+}
+
+-----------------------------------------------------------------
+5. Disconnecting
+
+Simply call mqtt_disconnect(client)
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/doc/ppp.txt b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/doc/ppp.txt
new file mode 100644
index 0000000..8b88b3a
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/doc/ppp.txt
@@ -0,0 +1,529 @@
+PPP interface for lwIP
+
+Author: Sylvain Rochet
+
+Table of Contents:
+
+1 - Supported PPP protocols and features
+2 - Raw API PPP example for all protocols
+3 - PPPoS input path (raw API, IRQ safe API, TCPIP API)
+4 - Thread safe PPP API (PPPAPI)
+5 - Notify phase callback (PPP_NOTIFY_PHASE)
+6 - Upgrading from lwIP <= 1.4.x to lwIP >= 2.0.x
+
+
+
+1 Supported PPP protocols and features
+======================================
+
+Supported Low level protocols:
+* PPP over serial using HDLC-like framing, such as wired dialup modems
+ or mobile telecommunications GPRS/EDGE/UMTS/HSPA+/LTE modems
+* PPP over Ethernet, such as xDSL modems
+* PPP over L2TP (Layer 2 Tunneling Protocol) LAC (L2TP Access Concentrator),
+ IP tunnel over UDP, such as VPN access
+
+Supported auth protocols:
+* PAP, Password Authentication Protocol
+* CHAP, Challenge-Handshake Authentication Protocol, also known as CHAP-MD5
+* MSCHAPv1, Microsoft version of CHAP, version 1
+* MSCHAPv2, Microsoft version of CHAP, version 2
+* EAP, Extensible Authentication Protocol
+
+Supported address protocols:
+* IPCP, IP Control Protocol, IPv4 addresses negotiation
+* IP6CP, IPv6 Control Protocol, IPv6 link-local addresses negotiation
+
+Supported encryption protocols:
+* MPPE, Microsoft Point-to-Point Encryption
+
+Supported compression or miscellaneous protocols, for serial links only:
+* PFC, Protocol Field Compression
+* ACFC, Address-and-Control-Field-Compression
+* ACCM, Asynchronous-Control-Character-Map
+* VJ, Van Jacobson TCP/IP Header Compression
+
+
+
+2 Raw API PPP example for all protocols
+=======================================
+
+As usual, raw API for lwIP means the lightweight API which *MUST* only be used
+for NO_SYS=1 systems or called inside lwIP core thread for NO_SYS=0 systems.
+
+/*
+ * Globals
+ * =======
+ */
+
+/* The PPP control block */
+ppp_pcb *ppp;
+
+/* The PPP IP interface */
+struct netif ppp_netif;
+
+
+/*
+ * PPP status callback
+ * ===================
+ *
+ * PPP status callback is called on PPP status change (up, down, …) from lwIP
+ * core thread
+ */
+
+/* PPP status callback example */
+static void status_cb(ppp_pcb *pcb, int err_code, void *ctx) {
+ struct netif *pppif = ppp_netif(pcb);
+ LWIP_UNUSED_ARG(ctx);
+
+ switch(err_code) {
+ case PPPERR_NONE: {
+#if LWIP_DNS
+ const ip_addr_t *ns;
+#endif /* LWIP_DNS */
+ printf("status_cb: Connected\n");
+#if PPP_IPV4_SUPPORT
+ printf(" our_ipaddr = %s\n", ipaddr_ntoa(&pppif->ip_addr));
+ printf(" his_ipaddr = %s\n", ipaddr_ntoa(&pppif->gw));
+ printf(" netmask = %s\n", ipaddr_ntoa(&pppif->netmask));
+#if LWIP_DNS
+ ns = dns_getserver(0);
+ printf(" dns1 = %s\n", ipaddr_ntoa(ns));
+ ns = dns_getserver(1);
+ printf(" dns2 = %s\n", ipaddr_ntoa(ns));
+#endif /* LWIP_DNS */
+#endif /* PPP_IPV4_SUPPORT */
+#if PPP_IPV6_SUPPORT
+ printf(" our6_ipaddr = %s\n", ip6addr_ntoa(netif_ip6_addr(pppif, 0)));
+#endif /* PPP_IPV6_SUPPORT */
+ break;
+ }
+ case PPPERR_PARAM: {
+ printf("status_cb: Invalid parameter\n");
+ break;
+ }
+ case PPPERR_OPEN: {
+ printf("status_cb: Unable to open PPP session\n");
+ break;
+ }
+ case PPPERR_DEVICE: {
+ printf("status_cb: Invalid I/O device for PPP\n");
+ break;
+ }
+ case PPPERR_ALLOC: {
+ printf("status_cb: Unable to allocate resources\n");
+ break;
+ }
+ case PPPERR_USER: {
+ printf("status_cb: User interrupt\n");
+ break;
+ }
+ case PPPERR_CONNECT: {
+ printf("status_cb: Connection lost\n");
+ break;
+ }
+ case PPPERR_AUTHFAIL: {
+ printf("status_cb: Failed authentication challenge\n");
+ break;
+ }
+ case PPPERR_PROTOCOL: {
+ printf("status_cb: Failed to meet protocol\n");
+ break;
+ }
+ case PPPERR_PEERDEAD: {
+ printf("status_cb: Connection timeout\n");
+ break;
+ }
+ case PPPERR_IDLETIMEOUT: {
+ printf("status_cb: Idle Timeout\n");
+ break;
+ }
+ case PPPERR_CONNECTTIME: {
+ printf("status_cb: Max connect time reached\n");
+ break;
+ }
+ case PPPERR_LOOPBACK: {
+ printf("status_cb: Loopback detected\n");
+ break;
+ }
+ default: {
+ printf("status_cb: Unknown error code %d\n", err_code);
+ break;
+ }
+ }
+
+/*
+ * This should be in the switch case, this is put outside of the switch
+ * case for example readability.
+ */
+
+ if (err_code == PPPERR_NONE) {
+ return;
+ }
+
+ /* ppp_close() was previously called, don't reconnect */
+ if (err_code == PPPERR_USER) {
+ /* ppp_free(); -- can be called here */
+ return;
+ }
+
+ /*
+ * Try to reconnect in 30 seconds, if you need a modem chatscript you have
+ * to do a much better signaling here ;-)
+ */
+ ppp_connect(pcb, 30);
+ /* OR ppp_listen(pcb); */
+}
+
+
+/*
+ * Creating a new PPPoS session
+ * ============================
+ *
+ * In lwIP, PPPoS is not PPPoSONET, in lwIP PPPoS is PPPoSerial.
+ */
+
+#include "netif/ppp/pppos.h"
+
+/*
+ * PPPoS serial output callback
+ *
+ * ppp_pcb, PPP control block
+ * data, buffer to write to serial port
+ * len, length of the data buffer
+ * ctx, optional user-provided callback context pointer
+ *
+ * Return value: len if write succeed
+ */
+static u32_t output_cb(ppp_pcb *pcb, u8_t *data, u32_t len, void *ctx) {
+ return uart_write(UART, data, len);
+}
+
+/*
+ * Create a new PPPoS interface
+ *
+ * ppp_netif, netif to use for this PPP link, i.e. PPP IP interface
+ * output_cb, PPPoS serial output callback
+ * status_cb, PPP status callback, called on PPP status change (up, down, …)
+ * ctx_cb, optional user-provided callback context pointer
+ */
+ppp = pppos_create(&ppp_netif,
+ output_cb, status_cb, ctx_cb);
+
+
+/*
+ * Creating a new PPPoE session
+ * ============================
+ */
+
+#include "netif/ppp/pppoe.h"
+
+/*
+ * Create a new PPPoE interface
+ *
+ * ppp_netif, netif to use for this PPP link, i.e. PPP IP interface
+ * ethif, already existing and setup Ethernet interface to use
+ * service_name, PPPoE service name discriminator (not supported yet)
+ * concentrator_name, PPPoE concentrator name discriminator (not supported yet)
+ * status_cb, PPP status callback, called on PPP status change (up, down, …)
+ * ctx_cb, optional user-provided callback context pointer
+ */
+ppp = pppoe_create(&ppp_netif,
+ &ethif,
+ service_name, concentrator_name,
+ status_cb, ctx_cb);
+
+
+/*
+ * Creating a new PPPoL2TP session
+ * ===============================
+ */
+
+#include "netif/ppp/pppol2tp.h"
+
+/*
+ * Create a new PPPoL2TP interface
+ *
+ * ppp_netif, netif to use for this PPP link, i.e. PPP IP interface
+ * netif, optional already existing and setup output netif, necessary if you
+ * want to set this interface as default route to settle the chicken
+ * and egg problem with VPN links
+ * ipaddr, IP to connect to
+ * port, UDP port to connect to (usually 1701)
+ * secret, L2TP secret to use
+ * secret_len, size in bytes of the L2TP secret
+ * status_cb, PPP status callback, called on PPP status change (up, down, …)
+ * ctx_cb, optional user-provided callback context pointer
+ */
+ppp = pppol2tp_create(&ppp_netif,
+ struct netif *netif, ip_addr_t *ipaddr, u16_t port,
+ u8_t *secret, u8_t secret_len,
+ ppp_link_status_cb_fn link_status_cb, void *ctx_cb);
+
+
+/*
+ * Initiate PPP client connection
+ * ==============================
+ */
+
+/* Set this interface as default route */
+ppp_set_default(ppp);
+
+/*
+ * Basic PPP client configuration. Can only be set if PPP session is in the
+ * dead state (i.e. disconnected). We don't need to provide thread-safe
+ * equivalents through PPPAPI because those helpers are only changing
+ * structure members while session is inactive for lwIP core. Configuration
+ * only need to be done once.
+ */
+
+/* Ask the peer for up to 2 DNS server addresses. */
+ppp_set_usepeerdns(ppp, 1);
+
+/* Auth configuration, this is pretty self-explanatory */
+ppp_set_auth(ppp, PPPAUTHTYPE_ANY, "login", "password");
+
+/*
+ * Initiate PPP negotiation, without waiting (holdoff=0), can only be called
+ * if PPP session is in the dead state (i.e. disconnected).
+ */
+u16_t holdoff = 0;
+ppp_connect(ppp, holdoff);
+
+
+/*
+ * Initiate PPP server listener
+ * ============================
+ */
+
+/*
+ * Basic PPP server configuration. Can only be set if PPP session is in the
+ * dead state (i.e. disconnected). We don't need to provide thread-safe
+ * equivalents through PPPAPI because those helpers are only changing
+ * structure members while session is inactive for lwIP core. Configuration
+ * only need to be done once.
+ */
+ip4_addr_t addr;
+
+/* Set our address */
+IP4_ADDR(&addr, 192,168,0,1);
+ppp_set_ipcp_ouraddr(ppp, &addr);
+
+/* Set peer(his) address */
+IP4_ADDR(&addr, 192,168,0,2);
+ppp_set_ipcp_hisaddr(ppp, &addr);
+
+/* Set primary DNS server */
+IP4_ADDR(&addr, 192,168,10,20);
+ppp_set_ipcp_dnsaddr(ppp, 0, &addr);
+
+/* Set secondary DNS server */
+IP4_ADDR(&addr, 192,168,10,21);
+ppp_set_ipcp_dnsaddr(ppp, 1, &addr);
+
+/* Auth configuration, this is pretty self-explanatory */
+ppp_set_auth(ppp, PPPAUTHTYPE_ANY, "login", "password");
+
+/* Require peer to authenticate */
+ppp_set_auth_required(ppp, 1);
+
+/*
+ * Only for PPPoS, the PPP session should be up and waiting for input.
+ *
+ * Note: for PPPoS, ppp_connect() and ppp_listen() are actually the same thing.
+ * The listen call is meant for future support of PPPoE and PPPoL2TP server
+ * mode, where we will need to negotiate the incoming PPPoE session or L2TP
+ * session before initiating PPP itself. We need this call because there is
+ * two passive modes for PPPoS, ppp_set_passive and ppp_set_silent.
+ */
+ppp_set_silent(pppos, 1);
+
+/*
+ * Initiate PPP listener (i.e. wait for an incoming connection), can only
+ * be called if PPP session is in the dead state (i.e. disconnected).
+ */
+ppp_listen(ppp);
+
+
+/*
+ * Closing PPP connection
+ * ======================
+ */
+
+/*
+ * Initiate the end of the PPP session, without carrier lost signal
+ * (nocarrier=0), meaning a clean shutdown of PPP protocols.
+ * You can call this function at anytime.
+ */
+u8_t nocarrier = 0;
+ppp_close(ppp, nocarrier);
+/*
+ * Then you must wait your status_cb() to be called, it may takes from a few
+ * seconds to several tens of seconds depending on the current PPP state.
+ */
+
+/*
+ * Freeing a PPP connection
+ * ========================
+ */
+
+/*
+ * Free the PPP control block, can only be called if PPP session is in the
+ * dead state (i.e. disconnected). You need to call ppp_close() before.
+ */
+ppp_free(ppp);
+
+
+
+3 PPPoS input path (raw API, IRQ safe API, TCPIP API)
+=====================================================
+
+Received data on serial port should be sent to lwIP using the pppos_input()
+function or the pppos_input_tcpip() function.
+
+If NO_SYS is 1 and if PPP_INPROC_IRQ_SAFE is 0 (the default), pppos_input()
+is not IRQ safe and then *MUST* only be called inside your main loop.
+
+Whatever the NO_SYS value, if PPP_INPROC_IRQ_SAFE is 1, pppos_input() is IRQ
+safe and can be safely called from an interrupt context, using that is going
+to reduce your need of buffer if pppos_input() is called byte after byte in
+your rx serial interrupt.
+
+if NO_SYS is 0, the thread safe way outside an interrupt context is to use
+the pppos_input_tcpip() function to pass input data to the lwIP core thread
+using the TCPIP API. This is thread safe in all cases but you should avoid
+passing data byte after byte because it uses heavy locking (mailbox) and it
+allocates pbuf, better fill them !
+
+if NO_SYS is 0 and if PPP_INPROC_IRQ_SAFE is 1, you may also use pppos_input()
+from an RX thread, however pppos_input() is not thread safe by itself. You can
+do that *BUT* you should NEVER call pppos_connect(), pppos_listen() and
+ppp_free() if pppos_input() can still be running, doing this is NOT thread safe
+at all. Using PPP_INPROC_IRQ_SAFE from an RX thread is discouraged unless you
+really know what you are doing, your move ;-)
+
+
+/*
+ * Fonction to call for received data
+ *
+ * ppp, PPP control block
+ * buffer, input buffer
+ * buffer_len, buffer length in bytes
+ */
+void pppos_input(ppp, buffer, buffer_len);
+
+or
+
+void pppos_input_tcpip(ppp, buffer, buffer_len);
+
+
+
+4 Thread safe PPP API (PPPAPI)
+==============================
+
+There is a thread safe API for all corresponding ppp_* functions, you have to
+enable LWIP_PPP_API in your lwipopts.h file, then see
+include/netif/ppp/pppapi.h, this is actually pretty obvious.
+
+
+
+5 Notify phase callback (PPP_NOTIFY_PHASE)
+==========================================
+
+Notify phase callback, enabled using the PPP_NOTIFY_PHASE config option, let
+you configure a callback that is called on each PPP internal state change.
+This is different from the status callback which only warns you about
+up(running) and down(dead) events.
+
+Notify phase callback can be used, for example, to set a LED pattern depending
+on the current phase of the PPP session. Here is a callback example which
+tries to mimic what we usually see on xDSL modems while they are negotiating
+the link, which should be self-explanatory:
+
+static void ppp_notify_phase_cb(ppp_pcb *pcb, u8_t phase, void *ctx) {
+ switch (phase) {
+
+ /* Session is down (either permanently or briefly) */
+ case PPP_PHASE_DEAD:
+ led_set(PPP_LED, LED_OFF);
+ break;
+
+ /* We are between two sessions */
+ case PPP_PHASE_HOLDOFF:
+ led_set(PPP_LED, LED_SLOW_BLINK);
+ break;
+
+ /* Session just started */
+ case PPP_PHASE_INITIALIZE:
+ led_set(PPP_LED, LED_FAST_BLINK);
+ break;
+
+ /* Session is running */
+ case PPP_PHASE_RUNNING:
+ led_set(PPP_LED, LED_ON);
+ break;
+
+ default:
+ break;
+ }
+}
+
+
+
+6 Upgrading from lwIP <= 1.4.x to lwIP >= 2.0.x
+===============================================
+
+PPP API was fully reworked between 1.4.x and 2.0.x releases. However porting
+from previous lwIP version is pretty easy:
+
+* Previous PPP API used an integer to identify PPP sessions, we are now
+ using ppp_pcb* control block, therefore all functions changed from "int ppp"
+ to "ppp_pcb *ppp"
+
+* struct netif was moved outside the PPP structure, you have to provide a netif
+ for PPP interface in pppoX_create() functions
+
+* PPP session are not started automatically after you created them anymore,
+ you have to call ppp_connect(), this way you can configure the session before
+ starting it.
+
+* Previous PPP API used CamelCase, we are now using snake_case.
+
+* Previous PPP API mixed PPPoS and PPPoE calls, this isn't the case anymore,
+ PPPoS functions are now prefixed pppos_ and PPPoE functions are now prefixed
+ pppoe_, common functions are now prefixed ppp_.
+
+* New PPPERR_ error codes added, check you have all of them in your status
+ callback function
+
+* Only the following include files should now be used in user application:
+ #include "netif/ppp/pppapi.h"
+ #include "netif/ppp/pppos.h"
+ #include "netif/ppp/pppoe.h"
+ #include "netif/ppp/pppol2tp.h"
+
+ Functions from ppp.h can be used, but you don't need to include this header
+ file as it is already included by above header files.
+
+* PPP_INPROC_OWNTHREAD was broken by design and was removed, you have to create
+ your own serial rx thread
+
+* PPP_INPROC_MULTITHREADED option was misnamed and confusing and was renamed
+ PPP_INPROC_IRQ_SAFE, please read the "PPPoS input path" documentation above
+ because you might have been fooled by that
+
+* If you used tcpip_callback_with_block() on ppp_ functions you may wish to use
+ the PPPAPI API instead.
+
+* ppp_sighup and ppp_close functions were merged using an optional argument
+ "nocarrier" on ppp_close.
+
+* DNS servers are now only remotely asked if LWIP_DNS is set and if
+ ppp_set_usepeerdns() is set to true, they are now automatically registered
+ using the dns_setserver() function so you don't need to do that in the PPP
+ callback anymore.
+
+* PPPoS does not use the SIO API anymore, as such it now requires a serial
+ output callback in place of sio_write
+
+* PPP_MAXIDLEFLAG is now in ms instead of jiffies
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/doc/rawapi.txt b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/doc/rawapi.txt
new file mode 100644
index 0000000..0cdfdce
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/doc/rawapi.txt
@@ -0,0 +1,499 @@
+Raw TCP/IP interface for lwIP
+
+Authors: Adam Dunkels, Leon Woestenberg, Christiaan Simons
+
+lwIP provides three Application Program's Interfaces (APIs) for programs
+to use for communication with the TCP/IP code:
+* low-level "core" / "callback" or "raw" API.
+* higher-level "sequential" API.
+* BSD-style socket API.
+
+The raw API (sometimes called native API) is an event-driven API designed
+to be used without an operating system that implements zero-copy send and
+receive. This API is also used by the core stack for interaction between
+the various protocols. It is the only API available when running lwIP
+without an operating system.
+
+The sequential API provides a way for ordinary, sequential, programs
+to use the lwIP stack. It is quite similar to the BSD socket API. The
+model of execution is based on the blocking open-read-write-close
+paradigm. Since the TCP/IP stack is event based by nature, the TCP/IP
+code and the application program must reside in different execution
+contexts (threads).
+
+The socket API is a compatibility API for existing applications,
+currently it is built on top of the sequential API. It is meant to
+provide all functions needed to run socket API applications running
+on other platforms (e.g. unix / windows etc.). However, due to limitations
+in the specification of this API, there might be incompatibilities
+that require small modifications of existing programs.
+
+** Multithreading
+
+lwIP started targeting single-threaded environments. When adding multi-
+threading support, instead of making the core thread-safe, another
+approach was chosen: there is one main thread running the lwIP core
+(also known as the "tcpip_thread"). When running in a multithreaded
+environment, raw API functions MUST only be called from the core thread
+since raw API functions are not protected from concurrent access (aside
+from pbuf- and memory management functions). Application threads using
+the sequential- or socket API communicate with this main thread through
+message passing.
+
+ As such, the list of functions that may be called from
+ other threads or an ISR is very limited! Only functions
+ from these API header files are thread-safe:
+ - api.h
+ - netbuf.h
+ - netdb.h
+ - netifapi.h
+ - pppapi.h
+ - sockets.h
+ - sys.h
+
+ Additionaly, memory (de-)allocation functions may be
+ called from multiple threads (not ISR!) with NO_SYS=0
+ since they are protected by SYS_LIGHTWEIGHT_PROT and/or
+ semaphores.
+
+ Netconn or Socket API functions are thread safe against the
+ core thread but they are not reentrant at the control block
+ granularity level. That is, a UDP or TCP control block must
+ not be shared among multiple threads without proper locking.
+
+ If SYS_LIGHTWEIGHT_PROT is set to 1 and
+ LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT is set to 1,
+ pbuf_free() may also be called from another thread or
+ an ISR (since only then, mem_free - for PBUF_RAM - may
+ be called from an ISR: otherwise, the HEAP is only
+ protected by semaphores).
+
+
+** The remainder of this document discusses the "raw" API. **
+
+The raw TCP/IP interface allows the application program to integrate
+better with the TCP/IP code. Program execution is event based by
+having callback functions being called from within the TCP/IP
+code. The TCP/IP code and the application program both run in the same
+thread. The sequential API has a much higher overhead and is not very
+well suited for small systems since it forces a multithreaded paradigm
+on the application.
+
+The raw TCP/IP interface is not only faster in terms of code execution
+time but is also less memory intensive. The drawback is that program
+development is somewhat harder and application programs written for
+the raw TCP/IP interface are more difficult to understand. Still, this
+is the preferred way of writing applications that should be small in
+code size and memory usage.
+
+All APIs can be used simultaneously by different application
+programs. In fact, the sequential API is implemented as an application
+program using the raw TCP/IP interface.
+
+Do not confuse the lwIP raw API with raw Ethernet or IP sockets.
+The former is a way of interfacing the lwIP network stack (including
+TCP and UDP), the later refers to processing raw Ethernet or IP data
+instead of TCP connections or UDP packets.
+
+Raw API applications may never block since all packet processing
+(input and output) as well as timer processing (TCP mainly) is done
+in a single execution context.
+
+--- Callbacks
+
+Program execution is driven by callbacks functions, which are then
+invoked by the lwIP core when activity related to that application
+occurs. A particular application may register to be notified via a
+callback function for events such as incoming data available, outgoing
+data sent, error notifications, poll timer expiration, connection
+closed, etc. An application can provide a callback function to perform
+processing for any or all of these events. Each callback is an ordinary
+C function that is called from within the TCP/IP code. Every callback
+function is passed the current TCP or UDP connection state as an
+argument. Also, in order to be able to keep program specific state,
+the callback functions are called with a program specified argument
+that is independent of the TCP/IP state.
+
+The function for setting the application connection state is:
+
+- void tcp_arg(struct tcp_pcb *pcb, void *arg)
+
+ Specifies the program specific state that should be passed to all
+ other callback functions. The "pcb" argument is the current TCP
+ connection control block, and the "arg" argument is the argument
+ that will be passed to the callbacks.
+
+
+--- TCP connection setup
+
+The functions used for setting up connections is similar to that of
+the sequential API and of the BSD socket API. A new TCP connection
+identifier (i.e., a protocol control block - PCB) is created with the
+tcp_new() function. This PCB can then be either set to listen for new
+incoming connections or be explicitly connected to another host.
+
+- struct tcp_pcb *tcp_new(void)
+
+ Creates a new connection identifier (PCB). If memory is not
+ available for creating the new pcb, NULL is returned.
+
+- err_t tcp_bind(struct tcp_pcb *pcb, ip_addr_t *ipaddr,
+ u16_t port)
+
+ Binds the pcb to a local IP address and port number. The IP address
+ can be specified as IP_ADDR_ANY in order to bind the connection to
+ all local IP addresses.
+
+ If another connection is bound to the same port, the function will
+ return ERR_USE, otherwise ERR_OK is returned.
+
+- struct tcp_pcb *tcp_listen(struct tcp_pcb *pcb)
+
+ Commands a pcb to start listening for incoming connections. When an
+ incoming connection is accepted, the function specified with the
+ tcp_accept() function will be called. The pcb will have to be bound
+ to a local port with the tcp_bind() function.
+
+ The tcp_listen() function returns a new connection identifier, and
+ the one passed as an argument to the function will be
+ deallocated. The reason for this behavior is that less memory is
+ needed for a connection that is listening, so tcp_listen() will
+ reclaim the memory needed for the original connection and allocate a
+ new smaller memory block for the listening connection.
+
+ tcp_listen() may return NULL if no memory was available for the
+ listening connection. If so, the memory associated with the pcb
+ passed as an argument to tcp_listen() will not be deallocated.
+
+- struct tcp_pcb *tcp_listen_with_backlog(struct tcp_pcb *pcb, u8_t backlog)
+
+ Same as tcp_listen, but limits the number of outstanding connections
+ in the listen queue to the value specified by the backlog argument.
+ To use it, your need to set TCP_LISTEN_BACKLOG=1 in your lwipopts.h.
+
+- void tcp_accept(struct tcp_pcb *pcb,
+ err_t (* accept)(void *arg, struct tcp_pcb *newpcb,
+ err_t err))
+
+ Specified the callback function that should be called when a new
+ connection arrives on a listening connection.
+
+- err_t tcp_connect(struct tcp_pcb *pcb, ip_addr_t *ipaddr,
+ u16_t port, err_t (* connected)(void *arg,
+ struct tcp_pcb *tpcb,
+ err_t err));
+
+ Sets up the pcb to connect to the remote host and sends the
+ initial SYN segment which opens the connection.
+
+ The tcp_connect() function returns immediately; it does not wait for
+ the connection to be properly setup. Instead, it will call the
+ function specified as the fourth argument (the "connected" argument)
+ when the connection is established. If the connection could not be
+ properly established, either because the other host refused the
+ connection or because the other host didn't answer, the "err"
+ callback function of this pcb (registered with tcp_err, see below)
+ will be called.
+
+ The tcp_connect() function can return ERR_MEM if no memory is
+ available for enqueueing the SYN segment. If the SYN indeed was
+ enqueued successfully, the tcp_connect() function returns ERR_OK.
+
+
+--- Sending TCP data
+
+TCP data is sent by enqueueing the data with a call to
+tcp_write(). When the data is successfully transmitted to the remote
+host, the application will be notified with a call to a specified
+callback function.
+
+- err_t tcp_write(struct tcp_pcb *pcb, 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 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 omitted,
+ 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
+ the queue of outgoing segment is larger than the upper limit defined
+ in lwipopts.h. The number of bytes available in the output queue can
+ be retrieved with the tcp_sndbuf() function.
+
+ The proper way to use this function is to call the function with at
+ most tcp_sndbuf() bytes of data. If the function returns ERR_MEM,
+ the application should wait until some of the currently enqueued
+ data has been successfully received by the other host and try again.
+
+- void tcp_sent(struct tcp_pcb *pcb,
+ err_t (* sent)(void *arg, struct tcp_pcb *tpcb,
+ u16_t len))
+
+ Specifies the callback function that should be called when data has
+ successfully been received (i.e., acknowledged) by the remote
+ host. The len argument passed to the callback function gives the
+ amount bytes that was acknowledged by the last acknowledgment.
+
+
+--- Receiving TCP data
+
+TCP data reception is callback based - an application specified
+callback function is called when new data arrives. When the
+application has taken the data, it has to call the tcp_recved()
+function to indicate that TCP can advertise increase the receive
+window.
+
+- void tcp_recv(struct tcp_pcb *pcb,
+ err_t (* recv)(void *arg, struct tcp_pcb *tpcb,
+ struct pbuf *p, err_t err))
+
+ Sets the callback function that will be called when new data
+ arrives. The callback function will be passed a NULL pbuf to
+ indicate that the remote host has closed the connection. If
+ there are no errors and the callback function is to return
+ ERR_OK, then it must free the pbuf. Otherwise, it must not
+ free the pbuf so that lwIP core code can store it.
+
+- void tcp_recved(struct tcp_pcb *pcb, u16_t len)
+
+ Must be called when the application has received the data. The len
+ argument indicates the length of the received data.
+
+
+--- Application polling
+
+When a connection is idle (i.e., no data is either transmitted or
+received), lwIP will repeatedly poll the application by calling a
+specified callback function. This can be used either as a watchdog
+timer for killing connections that have stayed idle for too long, or
+as a method of waiting for memory to become available. For instance,
+if a call to tcp_write() has failed because memory wasn't available,
+the application may use the polling functionality to call tcp_write()
+again when the connection has been idle for a while.
+
+- void tcp_poll(struct tcp_pcb *pcb,
+ err_t (* poll)(void *arg, struct tcp_pcb *tpcb),
+ u8_t interval)
+
+ Specifies the polling interval and the callback function that should
+ be called to poll the application. The interval is specified in
+ number of TCP coarse grained timer shots, which typically occurs
+ twice a second. An interval of 10 means that the application would
+ be polled every 5 seconds.
+
+
+--- Closing and aborting connections
+
+- err_t tcp_close(struct tcp_pcb *pcb)
+
+ Closes the connection. The function may return ERR_MEM if no memory
+ was available for closing the connection. If so, the application
+ should wait and try again either by using the acknowledgment
+ callback or the polling functionality. If the close succeeds, the
+ function returns ERR_OK.
+
+ The pcb is deallocated by the TCP code after a call to tcp_close().
+
+- void tcp_abort(struct tcp_pcb *pcb)
+
+ Aborts the connection by sending a RST (reset) segment to the remote
+ host. The pcb is deallocated. This function never fails.
+
+ ATTENTION: When calling this from one of the TCP callbacks, make
+ sure you always return ERR_ABRT (and never return ERR_ABRT otherwise
+ or you will risk accessing deallocated memory or memory leaks!
+
+
+If a connection is aborted because of an error, the application is
+alerted of this event by the err callback. Errors that might abort a
+connection are when there is a shortage of memory. The callback
+function to be called is set using the tcp_err() function.
+
+- void tcp_err(struct tcp_pcb *pcb, void (* err)(void *arg,
+ err_t err))
+
+ The error callback function does not get the pcb passed to it as a
+ parameter since the pcb may already have been deallocated.
+
+
+--- UDP interface
+
+The UDP interface is similar to that of TCP, but due to the lower
+level of complexity of UDP, the interface is significantly simpler.
+
+- struct udp_pcb *udp_new(void)
+
+ Creates a new UDP pcb which can be used for UDP communication. The
+ pcb is not active until it has either been bound to a local address
+ or connected to a remote address.
+
+- void udp_remove(struct udp_pcb *pcb)
+
+ Removes and deallocates the pcb.
+
+- err_t udp_bind(struct udp_pcb *pcb, 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, ip_addr_t *ipaddr,
+ u16_t port)
+
+ Sets the remote end of the pcb. This function does not generate any
+ network traffic, but only set the remote address of the pcb.
+
+- err_t udp_disconnect(struct udp_pcb *pcb)
+
+ Remove the remote end of the pcb. This function does not generate
+ any network traffic, but only removes the remote address of the pcb.
+
+- err_t udp_send(struct udp_pcb *pcb, struct pbuf *p)
+
+ Sends the pbuf p. The pbuf is not deallocated.
+
+- void udp_recv(struct udp_pcb *pcb,
+ void (* recv)(void *arg, struct udp_pcb *upcb,
+ struct pbuf *p,
+ ip_addr_t *addr,
+ u16_t port),
+ void *recv_arg)
+
+ Specifies a callback function that should be called when a UDP
+ datagram is received.
+
+
+--- System initalization
+
+A truly complete and generic sequence for initializing the lwIP stack
+cannot be given because it depends on additional initializations for
+your runtime environment (e.g. timers).
+
+We can give you some idea on how to proceed when using the raw API.
+We assume a configuration using a single Ethernet netif and the
+UDP and TCP transport layers, IPv4 and the DHCP client.
+
+Call these functions in the order of appearance:
+
+- lwip_init()
+
+ Initialize the lwIP stack and all of its subsystems.
+
+- netif_add(struct netif *netif, const ip4_addr_t *ipaddr,
+ const ip4_addr_t *netmask, const ip4_addr_t *gw,
+ void *state, netif_init_fn init, netif_input_fn input)
+
+ Adds your network interface to the netif_list. Allocate a struct
+ netif and pass a pointer to this structure as the first argument.
+ Give pointers to cleared ip_addr structures when using DHCP,
+ or fill them with sane numbers otherwise. The state pointer may be NULL.
+
+ The init function pointer must point to a initialization function for
+ your Ethernet netif interface. The following code illustrates its use.
+
+ err_t netif_if_init(struct netif *netif)
+ {
+ u8_t i;
+
+ for (i = 0; i < ETHARP_HWADDR_LEN; i++) {
+ netif->hwaddr[i] = some_eth_addr[i];
+ }
+ init_my_eth_device();
+ return ERR_OK;
+ }
+
+ For Ethernet drivers, the input function pointer must point to the lwIP
+ function ethernet_input() declared in "netif/etharp.h". Other drivers
+ must use ip_input() declared in "lwip/ip.h".
+
+- netif_set_default(struct netif *netif)
+
+ Registers the default network interface.
+
+- netif_set_link_up(struct netif *netif)
+
+ This is the hardware link state; e.g. whether cable is plugged for wired
+ Ethernet interface. This function must be called even if you don't know
+ the current state. Having link up and link down events is optional but
+ DHCP and IPv6 discover benefit well from those events.
+
+- netif_set_up(struct netif *netif)
+
+ This is the administrative (= software) state of the netif, when the
+ netif is fully configured this function must be called.
+
+- dhcp_start(struct netif *netif)
+
+ Creates a new DHCP client for this interface on the first call.
+
+ You can peek in the netif->dhcp struct for the actual DHCP status.
+
+- sys_check_timeouts()
+
+ When the system is running, you have to periodically call
+ sys_check_timeouts() which will handle all timers for all protocols in
+ the stack; add this to your main loop or equivalent.
+
+
+--- Optimalization hints
+
+The first thing you want to optimize is the lwip_standard_checksum()
+routine from src/core/inet.c. You can override this standard
+function with the #define LWIP_CHKSUM <your_checksum_routine>.
+
+There are C examples given in inet.c or you might want to
+craft an assembly function for this. RFC1071 is a good
+introduction to this subject.
+
+Other significant improvements can be made by supplying
+assembly or inline replacements for htons() and htonl()
+if you're using a little-endian architecture.
+#define lwip_htons(x) <your_htons>
+#define lwip_htonl(x) <your_htonl>
+If you #define them to htons() and htonl(), you should
+#define LWIP_DONT_PROVIDE_BYTEORDER_FUNCTIONS to prevent lwIP from
+defining hton*/ntoh* compatibility macros.
+
+Check your network interface driver if it reads at
+a higher speed than the maximum wire-speed. If the
+hardware isn't serviced frequently and fast enough
+buffer overflows are likely to occur.
+
+E.g. when using the cs8900 driver, call cs8900if_service(ethif)
+as frequently as possible. When using an RTOS let the cs8900 interrupt
+wake a high priority task that services your driver using a binary
+semaphore or event flag. Some drivers might allow additional tuning
+to match your application and network.
+
+For a production release it is recommended to set LWIP_STATS to 0.
+Note that speed performance isn't influenced much by simply setting
+high values to the memory options.
+
+For more optimization hints take a look at the lwIP wiki.
+
+--- Zero-copy MACs
+
+To achieve zero-copy on transmit, the data passed to the raw API must
+remain unchanged until sent. Because the send- (or write-)functions return
+when the packets have been enqueued for sending, data must be kept stable
+after that, too.
+
+This implies that PBUF_RAM/PBUF_POOL pbufs passed to raw-API send functions
+must *not* be reused by the application unless their ref-count is 1.
+
+For no-copy pbufs (PBUF_ROM/PBUF_REF), data must be kept unchanged, too,
+but the stack/driver will/must copy PBUF_REF'ed data when enqueueing, while
+PBUF_ROM-pbufs are just enqueued (as ROM-data is expected to never change).
+
+Also, data passed to tcp_write without the copy-flag must not be changed!
+
+Therefore, be careful which type of PBUF you use and if you copy TCP data
+or not!
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/doc/savannah.txt b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/doc/savannah.txt
new file mode 100644
index 0000000..d7d19eb
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/doc/savannah.txt
@@ -0,0 +1,120 @@
+Daily Use Guide for using Savannah for lwIP
+
+Table of Contents:
+
+1 - Obtaining lwIP from the Git repository
+2 - Committers/developers Git access using SSH
+3 - Merging a development branch to master branch
+4 - How to release lwIP
+
+
+
+1 Obtaining lwIP from the Git repository
+----------------------------------------
+
+To perform an anonymous Git clone of the master branch (this is where
+bug fixes and incremental enhancements occur), do this:
+ git clone git://git.savannah.nongnu.org/lwip.git
+
+Or, obtain a stable branch (updated with bug fixes only) as follows:
+ git clone --branch DEVEL-1_4_1 git://git.savannah.nongnu.org/lwip.git
+
+Or, obtain a specific (fixed) release as follows:
+ git clone --branch STABLE-1_4_1 git://git.savannah.nongnu.org/lwip.git
+
+
+2 Committers/developers Git access using SSH
+--------------------------------------------
+
+The Savannah server uses SSH (Secure Shell) protocol 2 authentication and encryption.
+As such, Git commits to the server occur through a SSH tunnel for project members.
+To create a SSH2 key pair in UNIX-like environments, do this:
+ ssh-keygen -t dsa
+
+Under Windows, a recommended SSH client is "PuTTY", freely available with good
+documentation and a graphic user interface. Use its key generator.
+
+Now paste the id_dsa.pub contents into your Savannah account public key list. Wait
+a while so that Savannah can update its configuration (This can take minutes).
+
+Try to login using SSH:
+ ssh -v your_login@git.sv.gnu.org
+
+If it tells you:
+ Linux vcs.savannah.gnu.org 2.6.32-5-xen-686 #1 SMP Wed Jun 17 17:10:03 UTC 2015 i686
+
+ Interactive shell login is not possible for security reasons.
+ VCS commands are allowed.
+ Last login: Tue May 15 23:10:12 2012 from 82.245.102.129
+ You tried to execute:
+ Sorry, you are not allowed to execute that command.
+ Shared connection to git.sv.gnu.org closed.
+
+then you could login; Savannah refuses to give you a shell - which is OK, as we
+are allowed to use SSH for Git only. Now, you should be able to do this:
+ git clone your_login@git.sv.gnu.org:/srv/git/lwip.git
+
+After which you can edit your local files with bug fixes or new features and
+commit them. Make sure you know what you are doing when using Git to make
+changes on the repository. If in doubt, ask on the lwip-members mailing list.
+
+(If SSH asks about authenticity of the host, you can check the key
+fingerprint against https://savannah.nongnu.org/git/?group=lwip
+
+
+3 - Merging a development branch to master branch
+-------------------------------------------------
+
+Merging is a straightforward process in Git. How to merge all changes in a
+development branch since our last merge from main:
+
+Checkout the master branch:
+ git checkout master
+
+Merge the development branch to master:
+ git merge your-development-branch
+
+Resolve any conflict.
+
+Commit the merge result.
+ git commit -a
+
+Push your commits:
+ git push
+
+
+4 How to release lwIP
+---------------------
+
+First, tag the release using Git: (I use release number 1.4.1 throughout
+this example).
+ git tag -a STABLE-1_4_1
+
+Share the tag reference by pushing it to remote:
+ git push origin STABLE-1_4_1
+
+Prepare the release:
+ cp -r lwip lwip-1.4.1
+ rm -rf lwip-1.4.1/.git lwip-1.4.1/.gitattributes
+
+Archive the current directory using tar, gzip'd, bzip2'd and zip'd.
+ tar czvf lwip-1.4.1.tar.gz lwip-1.4.1
+ tar cjvf lwip-1.4.1.tar.bz2 lwip-1.4.1
+ zip -r lwip-1.4.1.zip lwip-1.4.1
+
+Now, sign the archives with a detached GPG binary signature as follows:
+ gpg -b lwip-1.4.1.tar.gz
+ gpg -b lwip-1.4.1.tar.bz2
+ gpg -b lwip-1.4.1.zip
+
+Upload these files using anonymous FTP:
+ ncftp ftp://savannah.gnu.org/incoming/savannah/lwip
+ ncftp> mput *1.4.1.*
+
+Additionally, you may post a news item on Savannah, like this:
+
+A new 1.4.1 release is now available here:
+http://savannah.nongnu.org/files/?group=lwip&highlight=1.4.1
+
+You will have to submit this via the user News interface, then approve
+this via the Administrator News interface.
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/doc/snmp_agent.txt b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/doc/snmp_agent.txt
new file mode 100644
index 0000000..2653230
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/doc/snmp_agent.txt
@@ -0,0 +1,181 @@
+SNMPv1 agent for lwIP
+
+Author: Christiaan Simons
+
+This is a brief introduction how to use and configure the SNMP agent.
+Note the agent uses the raw-API UDP interface so you may also want to
+read rawapi.txt to gain a better understanding of the SNMP message handling.
+
+0 Agent Capabilities
+====================
+
+SNMPv1 per RFC1157
+ This is an old(er) standard but is still widely supported.
+ For SNMPv2c and v3 have a greater complexity and need many
+ more lines of code. IMHO this breaks the idea of "lightweight IP".
+
+ Note the S in SNMP stands for "Simple". Note that "Simple" is
+ relative. SNMP is simple compared to the complex ISO network
+ management protocols CMIP (Common Management Information Protocol)
+ and CMOT (CMip Over Tcp).
+
+MIB II per RFC1213
+ The standard lwIP stack management information base.
+ This is a required MIB, so this is always enabled.
+ When builing lwIP without TCP, the mib-2.tcp group is omitted.
+ The groups EGP, CMOT and transmission are disabled by default.
+
+ Most mib-2 objects are not writable except:
+ sysName, sysLocation, sysContact, snmpEnableAuthenTraps.
+ Writing to or changing the ARP and IP address and route
+ tables is not possible.
+
+ Note lwIP has a very limited notion of IP routing. It currently
+ doen't have a route table and doesn't have a notion of the U,G,H flags.
+ Instead lwIP uses the interface list with only one default interface
+ acting as a single gateway interface (G) for the default route.
+
+ The agent returns a "virtual table" with the default route 0.0.0.0
+ for the default interface and network routes (no H) for each
+ network interface in the netif_list.
+ All routes are considered to be up (U).
+
+Loading additional MIBs
+ MIBs can only be added in compile-time, not in run-time.
+ There is no MIB compiler thus additional MIBs must be hand coded.
+
+Large SNMP message support
+ The packet decoding and encoding routines are designed
+ to use pbuf-chains. Larger payloads than the minimum
+ SNMP requirement of 484 octets are supported if the
+ PBUF_POOL_SIZE and IP_REASS_BUFSIZE are set to match your
+ local requirement.
+
+1 Building the Agent
+====================
+
+First of all you'll need to add the following define
+to your local lwipopts.h:
+
+#define LWIP_SNMP 1
+
+and add the source files in lwip/src/core/snmp
+and some snmp headers in lwip/src/include/lwip to your makefile.
+
+Note you'll might need to adapt you network driver to update
+the mib2 variables for your interface.
+
+2 Running the Agent
+===================
+
+The following function calls must be made in your program to
+actually get the SNMP agent running.
+
+Before starting the agent you should supply pointers
+to non-volatile memory for sysContact, sysLocation,
+and snmpEnableAuthenTraps. You can do this by calling
+
+snmp_set_syscontact()
+snmp_set_syslocation()
+snmp_set_snmpenableauthentraps()
+
+Additionally you may want to set
+
+snmp_set_sysdescr()
+snmp_set_sysobjid() (if you have a private MIB)
+snmp_set_sysname()
+
+Also before starting the agent you need to setup
+one or more trap destinations using these calls:
+
+snmp_trap_dst_enable();
+snmp_trap_dst_ip_set();
+
+In the lwIP initialisation sequence call snmp_init() just after
+the call to udp_init().
+
+Exactly every 10 msec the SNMP uptime timestamp must be updated with
+snmp_inc_sysuptime(). You should call this from a timer interrupt
+or a timer signal handler depending on your runtime environment.
+
+An alternative way to update the SNMP uptime timestamp is to do a call like
+snmp_add_sysuptime(100) each 1000ms (which is bigger "step", but call to
+a lower frequency). Another one is to not call snmp_inc_sysuptime() or
+snmp_add_sysuptime(), and to define the SNMP_GET_SYSUPTIME(sysuptime) macro.
+This one is undefined by default in mib2.c. SNMP_GET_SYSUPTIME is called inside
+snmp_get_sysuptime(u32_t *value), and enable to change "sysuptime" value only
+when it's queried (any function which need "sysuptime" have to call
+snmp_get_sysuptime).
+
+
+3 Private MIBs
+==============
+
+If want to extend the agent with your own private MIB you'll need to
+add the following define to your local lwipopts.h:
+
+#define SNMP_PRIVATE_MIB 1
+
+You must provide the private_mib.h and associated files yourself.
+Note we don't have a "MIB compiler" that generates C source from a MIB,
+so you're required to do some serious coding if you enable this!
+
+Note the lwIP enterprise ID (26381) is assigned to the lwIP project,
+ALL OBJECT IDENTIFIERS LIVING UNDER THIS ID ARE ASSIGNED BY THE lwIP
+MAINTAINERS!
+
+If you need to create your own private MIB you'll need
+to apply for your own enterprise ID with IANA: http://www.iana.org/numbers.html
+
+You can set it by passing a struct snmp_obj_id to the agent
+using snmp_set_sysobjid(&my_object_id), just before snmp_init().
+
+Note the object identifiers for thes MIB-2 and your private MIB
+tree must be kept in sorted ascending (lexicographical) order.
+This to ensure correct getnext operation.
+
+An example for a private MIB is part of the "minimal Unix" project:
+contrib/ports/unix/proj/minimal/lwip_prvmib.c
+
+The next chapter gives a more detailed description of the
+MIB-2 tree and the optional private MIB.
+
+4 The Gory Details
+==================
+
+4.0 Object identifiers and the MIB tree.
+
+We have three distinct parts for all object identifiers:
+
+The prefix
+ .iso.org.dod.internet
+
+the middle part
+ .mgmt.mib-2.ip.ipNetToMediaTable.ipNetToMediaEntry.ipNetToMediaPhysAddress
+
+and the index part
+ .1.192.168.0.1
+
+Objects located above the .internet hierarchy aren't supported.
+Currently only the .mgmt sub-tree is available and
+when the SNMP_PRIVATE_MIB is enabled the .private tree
+becomes available too.
+
+Object identifiers from incoming requests are checked
+for a matching prefix, middle part and index part
+or are expanded(*) for GetNext requests with short
+or inexisting names in the request.
+(* we call this "expansion" but this also
+resembles the "auto-completion" operation)
+
+The middle part is usually located in ROM (const)
+to preserve precious RAM on small microcontrollers.
+However RAM location is possible for a dynamically
+changing private tree.
+
+The index part is handled by functions which in
+turn use dynamically allocated index trees from RAM.
+These trees are updated by e.g. the etharp code
+when new entries are made or removed form the ARP cache.
+
+/** @todo more gory details */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/doc/sys_arch.txt b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/doc/sys_arch.txt
new file mode 100644
index 0000000..4dc727b
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/doc/sys_arch.txt
@@ -0,0 +1,303 @@
+sys_arch interface for lwIP
+
+Author: Adam Dunkels
+ Simon Goldschmidt
+
+The operating system emulation layer provides a common interface
+between the lwIP code and the underlying operating system kernel. The
+general idea is that porting lwIP to new architectures requires only
+small changes to a few header files and a new sys_arch
+implementation. It is also possible to do a sys_arch implementation
+that does not rely on any underlying operating system.
+
+The sys_arch provides semaphores, mailboxes and mutexes to lwIP. For the full
+lwIP functionality, multiple threads support can be implemented in the
+sys_arch, but this is not required for the basic lwIP
+functionality. Timer scheduling is implemented in lwIP, but can be implemented
+by the sys_arch port (LWIP_TIMERS_CUSTOM==1).
+
+In addition to the source file providing the functionality of sys_arch,
+the OS emulation layer must provide several header files defining
+macros used throughout lwip. The files required and the macros they
+must define are listed below the sys_arch description.
+
+Semaphores can be either counting or binary - lwIP works with both
+kinds. Mailboxes should be implemented as a queue which allows multiple messages
+to be posted (implementing as a rendez-vous point where only one message can be
+posted at a time can have a highly negative impact on performance). A message
+in a mailbox is just a pointer, nothing more.
+
+Semaphores are represented by the type "sys_sem_t" which is typedef'd
+in the sys_arch.h file. Mailboxes are equivalently represented by the
+type "sys_mbox_t". Mutexes are represented by the type "sys_mutex_t".
+lwIP does not place any restrictions on how these types are represented
+internally.
+
+Since lwIP 1.4.0, semaphore, mutexes 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.
+
+- err_t sys_sem_new(sys_sem_t *sem, u8_t count)
+
+ 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)
+
+ Deallocates a semaphore.
+
+- void sys_sem_signal(sys_sem_t *sem)
+
+ Signals a semaphore.
+
+- u32_t sys_arch_sem_wait(sys_sem_t *sem, u32_t timeout)
+
+ Blocks the thread while waiting for the semaphore to be
+ signaled. If the "timeout" argument is non-zero, the thread should
+ only be blocked for the specified time (measured in
+ milliseconds). If the "timeout" argument is zero, the thread should be
+ blocked until the semaphore is signalled.
+
+ If the timeout argument is non-zero, the return value is the number of
+ milliseconds spent waiting for the semaphore to be signaled. If the
+ semaphore wasn't signaled within the specified time, the return value is
+ SYS_ARCH_TIMEOUT. If the thread didn't have to wait for the semaphore
+ (i.e., it was already signaled), the function may return zero.
+
+ Notice that lwIP implements a function with a similar name,
+ sys_sem_wait(), that uses the sys_arch_sem_wait() function.
+
+- 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.
+
+- void sys_mutex_new(sys_mutex_t *mutex)
+
+ Creates a new mutex. The mutex is allocated to the memory that 'mutex'
+ points to (which can be both a pointer or the actual OS structure).
+ If the mutex 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_mutex_free(sys_mutex_t *mutex)
+
+ Deallocates a mutex.
+
+- void sys_mutex_lock(sys_mutex_t *mutex)
+
+ Blocks the thread until the mutex can be grabbed.
+
+- void sys_mutex_unlock(sys_mutex_t *mutex)
+
+ Releases the mutex previously locked through 'sys_mutex_lock()'.
+
+- void sys_mutex_valid(sys_mutex_t *mutex)
+
+ Returns 1 if the mutes 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_mutex_set_invalid(sys_mutex_t *mutex)
+
+ Invalidate a mutex so that sys_mutex_valid() returns 0.
+ ATTENTION: This does NOT mean that the mutex shall be deallocated:
+ sys_mutex_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)
+
+ Deallocates a mailbox. If there are messages still present in the
+ mailbox when the mailbox is deallocated, it is an indication of a
+ programming error in lwIP and the developer should be notified.
+
+- void sys_mbox_post(sys_mbox_t *mbox, void *msg)
+
+ Posts the "msg" to the mailbox. This function have to block until
+ the "msg" is really posted.
+
+- err_t sys_mbox_trypost(sys_mbox_t *mbox, void *msg)
+
+ Try to post the "msg" to the mailbox. Returns ERR_MEM if this one
+ is full, else, ERR_OK if the "msg" is posted.
+
+- u32_t sys_arch_mbox_fetch(sys_mbox_t *mbox, void **msg, u32_t timeout)
+
+ Blocks the thread until a message arrives in the mailbox, but does
+ not block the thread longer than "timeout" milliseconds (similar to
+ the sys_arch_sem_wait() function). If "timeout" is 0, the thread should
+ be blocked until a message arrives. The "msg" argument is a result
+ parameter that is set by the function (i.e., by doing "*msg =
+ ptr"). The "msg" parameter maybe NULL to indicate that the message
+ should be dropped.
+
+ The return values are the same as for the sys_arch_sem_wait() function:
+ Number of milliseconds spent waiting or SYS_ARCH_TIMEOUT if there was a
+ timeout.
+
+ Note that a function with a similar name, sys_mbox_fetch(), is
+ implemented by lwIP.
+
+- u32_t sys_arch_mbox_tryfetch(sys_mbox_t *mbox, void **msg)
+
+ This is similar to sys_arch_mbox_fetch, however if a message is not
+ present in the mailbox, it immediately returns with the code
+ SYS_MBOX_EMPTY. On success 0 is returned.
+
+ To allow for efficient implementations, this can be defined as a
+ function-like macro in sys_arch.h instead of a normal function. For
+ example, a naive implementation could be:
+ #define sys_arch_mbox_tryfetch(mbox,msg) \
+ sys_arch_mbox_fetch(mbox,msg,1)
+ although this would introduce unnecessary delays.
+
+- 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:
+
+- sys_thread_t sys_thread_new(char *name, void (* thread)(void *arg), void *arg, int stacksize, int prio)
+
+ Starts a new thread named "name" with priority "prio" that will begin its
+ execution in the function "thread()". The "arg" argument will be passed as an
+ argument to the thread() function. The stack size to used for this thread is
+ the "stacksize" parameter. The id of the new thread is returned. Both the id
+ and the priority are system dependent.
+
+When lwIP is used from more than one context (e.g. from multiple threads OR from
+main-loop and from interrupts), the SYS_LIGHTWEIGHT_PROT protection SHOULD be enabled!
+
+- sys_prot_t sys_arch_protect(void)
+
+ This optional function does a "fast" critical region protection and returns
+ the previous protection level. This function is only called during very short
+ critical regions. An embedded system which supports ISR-based drivers might
+ want to implement this function by disabling interrupts. Task-based systems
+ might want to implement this by using a mutex or disabling tasking. This
+ function should support recursive calls from the same task or interrupt. In
+ other words, sys_arch_protect() could be called while already protected. In
+ that case the return value indicates that it is already protected.
+
+ sys_arch_protect() is only required if your port is supporting an operating
+ system.
+
+- void sys_arch_unprotect(sys_prot_t pval)
+
+ This optional function does a "fast" set of critical region protection to the
+ value specified by pval. See the documentation for sys_arch_protect() for
+ more information. This function is only required if your port is supporting
+ an operating system.
+
+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 careful with using mem_malloc() in sys_arch. When malloc() refers to
+mem_malloc() you can run into a circular function call problem. In mem.c
+mem_init() tries to allcate a semaphore using mem_malloc, which of course
+can't be performed when sys_arch uses mem_malloc.
+
+-------------------------------------------------------------------------------
+Additional files required for the "OS support" emulation layer:
+-------------------------------------------------------------------------------
+
+cc.h - Architecture environment, some compiler specific, some
+ environment specific (probably should move env stuff
+ to sys_arch.h.)
+
+ Typedefs for the types used by lwip -
+ u8_t, s8_t, u16_t, s16_t, u32_t, s32_t, mem_ptr_t
+
+ Compiler hints for packing lwip's structures -
+ PACK_STRUCT_FIELD(x)
+ PACK_STRUCT_STRUCT
+ PACK_STRUCT_BEGIN
+ PACK_STRUCT_END
+
+ Platform specific diagnostic output -
+ LWIP_PLATFORM_DIAG(x) - non-fatal, print a message.
+ LWIP_PLATFORM_ASSERT(x) - fatal, print message and abandon execution.
+ Portability defines for printf formatters:
+ U16_F, S16_F, X16_F, U32_F, S32_F, X32_F, SZT_F
+
+ "lightweight" synchronization mechanisms -
+ SYS_ARCH_DECL_PROTECT(x) - declare a protection state variable.
+ SYS_ARCH_PROTECT(x) - enter protection mode.
+ SYS_ARCH_UNPROTECT(x) - leave protection mode.
+
+ If the compiler does not provide memset() this file must include a
+ definition of it, or include a file which defines it.
+
+ This file must either include a system-local <errno.h> which defines
+ the standard *nix error codes, or it should #define LWIP_PROVIDE_ERRNO
+ to make lwip/arch.h define the codes which are used throughout.
+
+
+perf.h - Architecture specific performance measurement.
+ Measurement calls made throughout lwip, these can be defined to nothing.
+ PERF_START - start measuring something.
+ PERF_STOP(x) - stop measuring something, and record the result.
+
+sys_arch.h - Tied to sys_arch.c
+
+ Arch dependent types for the following objects:
+ sys_sem_t, sys_mbox_t, sys_thread_t,
+ And, optionally:
+ sys_prot_t
+
+ Defines to set vars of sys_mbox_t and sys_sem_t to NULL.
+ SYS_MBOX_NULL NULL
+ SYS_SEM_NULL NULL
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/license.txt b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/license.txt
new file mode 100644
index 0000000..9f7258b
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/license.txt
@@ -0,0 +1,25 @@
+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.
+
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/FILES b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/FILES
new file mode 100644
index 0000000..0be0741
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/FILES
@@ -0,0 +1,15 @@
+api/ - The code for the high-level wrapper API. Not needed if
+ you use the lowel-level call-back/raw API.
+
+apps/ - Higher layer applications that are specifically programmed
+ with the lwIP low-level raw API.
+
+core/ - The core of the TPC/IP stack; protocol implementations,
+ memory and buffer management, and the low-level raw API.
+
+include/ - lwIP include files.
+
+netif/ - Generic network interface device drivers are kept here.
+
+For more information on the various subdirectories, check the FILES
+file in each directory.
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/Filelists.mk b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/Filelists.mk
new file mode 100644
index 0000000..7d30bb8
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/Filelists.mk
@@ -0,0 +1,181 @@
+#
+# 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>
+#
+
+# COREFILES, CORE4FILES: The minimum set of files needed for lwIP.
+COREFILES=$(LWIPDIR)/core/init.c \
+ $(LWIPDIR)/core/def.c \
+ $(LWIPDIR)/core/dns.c \
+ $(LWIPDIR)/core/inet_chksum.c \
+ $(LWIPDIR)/core/ip.c \
+ $(LWIPDIR)/core/mem.c \
+ $(LWIPDIR)/core/memp.c \
+ $(LWIPDIR)/core/netif.c \
+ $(LWIPDIR)/core/pbuf.c \
+ $(LWIPDIR)/core/raw.c \
+ $(LWIPDIR)/core/stats.c \
+ $(LWIPDIR)/core/sys.c \
+ $(LWIPDIR)/core/tcp.c \
+ $(LWIPDIR)/core/tcp_in.c \
+ $(LWIPDIR)/core/tcp_out.c \
+ $(LWIPDIR)/core/timeouts.c \
+ $(LWIPDIR)/core/udp.c
+
+CORE4FILES=$(LWIPDIR)/core/ipv4/autoip.c \
+ $(LWIPDIR)/core/ipv4/dhcp.c \
+ $(LWIPDIR)/core/ipv4/etharp.c \
+ $(LWIPDIR)/core/ipv4/icmp.c \
+ $(LWIPDIR)/core/ipv4/igmp.c \
+ $(LWIPDIR)/core/ipv4/ip4_frag.c \
+ $(LWIPDIR)/core/ipv4/ip4.c \
+ $(LWIPDIR)/core/ipv4/ip4_addr.c
+
+CORE6FILES=$(LWIPDIR)/core/ipv6/dhcp6.c \
+ $(LWIPDIR)/core/ipv6/ethip6.c \
+ $(LWIPDIR)/core/ipv6/icmp6.c \
+ $(LWIPDIR)/core/ipv6/inet6.c \
+ $(LWIPDIR)/core/ipv6/ip6.c \
+ $(LWIPDIR)/core/ipv6/ip6_addr.c \
+ $(LWIPDIR)/core/ipv6/ip6_frag.c \
+ $(LWIPDIR)/core/ipv6/mld6.c \
+ $(LWIPDIR)/core/ipv6/nd6.c
+
+# APIFILES: The files which implement the sequential and socket APIs.
+APIFILES=$(LWIPDIR)/api/api_lib.c \
+ $(LWIPDIR)/api/api_msg.c \
+ $(LWIPDIR)/api/err.c \
+ $(LWIPDIR)/api/netbuf.c \
+ $(LWIPDIR)/api/netdb.c \
+ $(LWIPDIR)/api/netifapi.c \
+ $(LWIPDIR)/api/sockets.c \
+ $(LWIPDIR)/api/tcpip.c
+
+# NETIFFILES: Files implementing various generic network interface functions
+NETIFFILES=$(LWIPDIR)/netif/ethernet.c \
+ $(LWIPDIR)/netif/slipif.c
+
+# SIXLOWPAN: 6LoWPAN
+SIXLOWPAN=$(LWIPDIR)/netif/lowpan6.c \
+
+# PPPFILES: PPP
+PPPFILES=$(LWIPDIR)/netif/ppp/auth.c \
+ $(LWIPDIR)/netif/ppp/ccp.c \
+ $(LWIPDIR)/netif/ppp/chap-md5.c \
+ $(LWIPDIR)/netif/ppp/chap_ms.c \
+ $(LWIPDIR)/netif/ppp/chap-new.c \
+ $(LWIPDIR)/netif/ppp/demand.c \
+ $(LWIPDIR)/netif/ppp/eap.c \
+ $(LWIPDIR)/netif/ppp/ecp.c \
+ $(LWIPDIR)/netif/ppp/eui64.c \
+ $(LWIPDIR)/netif/ppp/fsm.c \
+ $(LWIPDIR)/netif/ppp/ipcp.c \
+ $(LWIPDIR)/netif/ppp/ipv6cp.c \
+ $(LWIPDIR)/netif/ppp/lcp.c \
+ $(LWIPDIR)/netif/ppp/magic.c \
+ $(LWIPDIR)/netif/ppp/mppe.c \
+ $(LWIPDIR)/netif/ppp/multilink.c \
+ $(LWIPDIR)/netif/ppp/ppp.c \
+ $(LWIPDIR)/netif/ppp/pppapi.c \
+ $(LWIPDIR)/netif/ppp/pppcrypt.c \
+ $(LWIPDIR)/netif/ppp/pppoe.c \
+ $(LWIPDIR)/netif/ppp/pppol2tp.c \
+ $(LWIPDIR)/netif/ppp/pppos.c \
+ $(LWIPDIR)/netif/ppp/upap.c \
+ $(LWIPDIR)/netif/ppp/utils.c \
+ $(LWIPDIR)/netif/ppp/vj.c \
+ $(LWIPDIR)/netif/ppp/polarssl/arc4.c \
+ $(LWIPDIR)/netif/ppp/polarssl/des.c \
+ $(LWIPDIR)/netif/ppp/polarssl/md4.c \
+ $(LWIPDIR)/netif/ppp/polarssl/md5.c \
+ $(LWIPDIR)/netif/ppp/polarssl/sha1.c
+
+# LWIPNOAPPSFILES: All LWIP files without apps
+LWIPNOAPPSFILES=$(COREFILES) \
+ $(CORE4FILES) \
+ $(CORE6FILES) \
+ $(APIFILES) \
+ $(NETIFFILES) \
+ $(PPPFILES) \
+ $(SIXLOWPAN)
+
+# SNMPFILES: SNMPv2c agent
+SNMPFILES=$(LWIPDIR)/apps/snmp/snmp_asn1.c \
+ $(LWIPDIR)/apps/snmp/snmp_core.c \
+ $(LWIPDIR)/apps/snmp/snmp_mib2.c \
+ $(LWIPDIR)/apps/snmp/snmp_mib2_icmp.c \
+ $(LWIPDIR)/apps/snmp/snmp_mib2_interfaces.c \
+ $(LWIPDIR)/apps/snmp/snmp_mib2_ip.c \
+ $(LWIPDIR)/apps/snmp/snmp_mib2_snmp.c \
+ $(LWIPDIR)/apps/snmp/snmp_mib2_system.c \
+ $(LWIPDIR)/apps/snmp/snmp_mib2_tcp.c \
+ $(LWIPDIR)/apps/snmp/snmp_mib2_udp.c \
+ $(LWIPDIR)/apps/snmp/snmp_msg.c \
+ $(LWIPDIR)/apps/snmp/snmpv3.c \
+ $(LWIPDIR)/apps/snmp/snmp_netconn.c \
+ $(LWIPDIR)/apps/snmp/snmp_pbuf_stream.c \
+ $(LWIPDIR)/apps/snmp/snmp_raw.c \
+ $(LWIPDIR)/apps/snmp/snmp_scalar.c \
+ $(LWIPDIR)/apps/snmp/snmp_table.c \
+ $(LWIPDIR)/apps/snmp/snmp_threadsync.c \
+ $(LWIPDIR)/apps/snmp/snmp_traps.c \
+ $(LWIPDIR)/apps/snmp/snmpv3_mbedtls.c \
+ $(LWIPDIR)/apps/snmp/snmpv3_dummy.c
+
+# HTTPDFILES: HTTP server
+HTTPDFILES=$(LWIPDIR)/apps/httpd/fs.c \
+ $(LWIPDIR)/apps/httpd/httpd.c
+
+# LWIPERFFILES: IPERF server
+LWIPERFFILES=$(LWIPDIR)/apps/lwiperf/lwiperf.c
+
+# SNTPFILES: SNTP client
+SNTPFILES=$(LWIPDIR)/apps/sntp/sntp.c
+
+# MDNSFILES: MDNS responder
+MDNSFILES=$(LWIPDIR)/apps/mdns/mdns.c
+
+# NETBIOSNSFILES: NetBIOS name server
+NETBIOSNSFILES=$(LWIPDIR)/apps/netbiosns/netbiosns.c
+
+# TFTPFILES: TFTP server files
+TFTPFILES=$(LWIPDIR)/apps/tftp/tftp_server.c
+
+# MQTTFILES: MQTT client files
+MQTTFILES=$(LWIPDIR)/apps/mqtt/mqtt.c
+
+# LWIPAPPFILES: All LWIP APPs
+LWIPAPPFILES=$(SNMPFILES) \
+ $(HTTPDFILES) \
+ $(LWIPERFFILES) \
+ $(SNTPFILES) \
+ $(MDNSFILES) \
+ $(NETBIOSNSFILES) \
+ $(TFTPFILES) \
+ $(MQTTFILES)
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/api/api_lib.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/api/api_lib.c
new file mode 100644
index 0000000..3c1d6a6
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/api/api_lib.c
@@ -0,0 +1,1010 @@
+/**
+ * @file
+ * Sequential API External module
+ *
+ * @defgroup netconn Netconn API
+ * @ingroup sequential_api
+ * Thread-safe, to be called from non-TCPIP threads only.
+ * TX/RX handling based on @ref netbuf (containing @ref pbuf)
+ * to avoid copying data around.
+ *
+ * @defgroup netconn_common Common functions
+ * @ingroup netconn
+ * For use with TCP and UDP
+ *
+ * @defgroup netconn_tcp TCP only
+ * @ingroup netconn
+ * TCP only functions
+ *
+ * @defgroup netconn_udp UDP only
+ * @ingroup netconn
+ * UDP only 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>
+ */
+
+/* This is the part of the API that is linked with
+ the application */
+
+#include "lwip/opt.h"
+
+#if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/api.h"
+#include "lwip/memp.h"
+
+#include "lwip/ip.h"
+#include "lwip/raw.h"
+#include "lwip/udp.h"
+#include "lwip/priv/api_msg.h"
+#include "lwip/priv/tcp_priv.h"
+#include "lwip/priv/tcpip_priv.h"
+
+#include <string.h>
+
+#define API_MSG_VAR_REF(name) API_VAR_REF(name)
+#define API_MSG_VAR_DECLARE(name) API_VAR_DECLARE(struct api_msg, name)
+#define API_MSG_VAR_ALLOC(name) API_VAR_ALLOC(struct api_msg, MEMP_API_MSG, name, ERR_MEM)
+#define API_MSG_VAR_ALLOC_RETURN_NULL(name) API_VAR_ALLOC(struct api_msg, MEMP_API_MSG, name, NULL)
+#define API_MSG_VAR_FREE(name) API_VAR_FREE(MEMP_API_MSG, name)
+
+static err_t netconn_close_shutdown(struct netconn *conn, u8_t how);
+
+/**
+ * Call the lower part of a netconn_* function
+ * This function is then running in the thread context
+ * of tcpip_thread and has exclusive access to lwIP core code.
+ *
+ * @param fn function to call
+ * @param apimsg a struct containing the function to call and its parameters
+ * @return ERR_OK if the function was called, another err_t if not
+ */
+static err_t
+netconn_apimsg(tcpip_callback_fn fn, struct api_msg *apimsg)
+{
+ err_t err;
+
+#ifdef LWIP_DEBUG
+ /* catch functions that don't set err */
+ apimsg->err = ERR_VAL;
+#endif /* LWIP_DEBUG */
+
+#if LWIP_NETCONN_SEM_PER_THREAD
+ apimsg->op_completed_sem = LWIP_NETCONN_THREAD_SEM_GET();
+#endif /* LWIP_NETCONN_SEM_PER_THREAD */
+
+ err = tcpip_send_msg_wait_sem(fn, apimsg, LWIP_API_MSG_SEM(apimsg));
+ if (err == ERR_OK) {
+ return apimsg->err;
+ }
+ return err;
+}
+
+/**
+ * Create a new netconn (of a specific type) that has a callback function.
+ * The corresponding pcb is also created.
+ *
+ * @param t the type of 'connection' to create (@see enum netconn_type)
+ * @param proto the IP protocol for RAW IP pcbs
+ * @param callback a function to call on status changes (RX available, TX'ed)
+ * @return a newly allocated struct netconn or
+ * NULL on memory error
+ */
+struct netconn*
+netconn_new_with_proto_and_callback(enum netconn_type t, u8_t proto, netconn_callback callback)
+{
+ struct netconn *conn;
+ API_MSG_VAR_DECLARE(msg);
+ API_MSG_VAR_ALLOC_RETURN_NULL(msg);
+
+ conn = netconn_alloc(t, callback);
+ if (conn != NULL) {
+ err_t err;
+
+ API_MSG_VAR_REF(msg).msg.n.proto = proto;
+ API_MSG_VAR_REF(msg).conn = conn;
+ err = netconn_apimsg(lwip_netconn_do_newconn, &API_MSG_VAR_REF(msg));
+ if (err != ERR_OK) {
+ LWIP_ASSERT("freeing conn without freeing pcb", conn->pcb.tcp == NULL);
+ LWIP_ASSERT("conn has no recvmbox", sys_mbox_valid(&conn->recvmbox));
+#if LWIP_TCP
+ LWIP_ASSERT("conn->acceptmbox shouldn't exist", !sys_mbox_valid(&conn->acceptmbox));
+#endif /* LWIP_TCP */
+#if !LWIP_NETCONN_SEM_PER_THREAD
+ LWIP_ASSERT("conn has no op_completed", sys_sem_valid(&conn->op_completed));
+ sys_sem_free(&conn->op_completed);
+#endif /* !LWIP_NETCONN_SEM_PER_THREAD */
+ sys_mbox_free(&conn->recvmbox);
+ memp_free(MEMP_NETCONN, conn);
+ API_MSG_VAR_FREE(msg);
+ return NULL;
+ }
+ }
+ API_MSG_VAR_FREE(msg);
+ return conn;
+}
+
+/**
+ * @ingroup netconn_common
+ * Close a netconn 'connection' and free its resources.
+ * UDP and RAW connection are completely closed, TCP pcbs might still be in a waitstate
+ * after this returns.
+ *
+ * @param conn the netconn to delete
+ * @return ERR_OK if the connection was deleted
+ */
+err_t
+netconn_delete(struct netconn *conn)
+{
+ err_t err;
+ API_MSG_VAR_DECLARE(msg);
+
+ /* No ASSERT here because possible to get a (conn == NULL) if we got an accept error */
+ if (conn == NULL) {
+ return ERR_OK;
+ }
+
+ API_MSG_VAR_ALLOC(msg);
+ API_MSG_VAR_REF(msg).conn = conn;
+#if LWIP_SO_SNDTIMEO || LWIP_SO_LINGER
+ /* get the time we started, which is later compared to
+ sys_now() + conn->send_timeout */
+ API_MSG_VAR_REF(msg).msg.sd.time_started = sys_now();
+#else /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */
+#if LWIP_TCP
+ API_MSG_VAR_REF(msg).msg.sd.polls_left =
+ ((LWIP_TCP_CLOSE_TIMEOUT_MS_DEFAULT + TCP_SLOW_INTERVAL - 1) / TCP_SLOW_INTERVAL) + 1;
+#endif /* LWIP_TCP */
+#endif /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */
+ err = netconn_apimsg(lwip_netconn_do_delconn, &API_MSG_VAR_REF(msg));
+ API_MSG_VAR_FREE(msg);
+
+ if (err != ERR_OK) {
+ return err;
+ }
+
+ netconn_free(conn);
+
+ return ERR_OK;
+}
+
+/**
+ * Get the local or remote IP address and port of a netconn.
+ * For RAW netconns, this returns the protocol instead of a port!
+ *
+ * @param conn the netconn to query
+ * @param addr a pointer to which to save the IP address
+ * @param port a pointer to which to save the port (or protocol for RAW)
+ * @param local 1 to get the local IP address, 0 to get the remote one
+ * @return ERR_CONN for invalid connections
+ * ERR_OK if the information was retrieved
+ */
+err_t
+netconn_getaddr(struct netconn *conn, ip_addr_t *addr, u16_t *port, u8_t local)
+{
+ API_MSG_VAR_DECLARE(msg);
+ err_t err;
+
+ LWIP_ERROR("netconn_getaddr: invalid conn", (conn != NULL), return ERR_ARG;);
+ LWIP_ERROR("netconn_getaddr: invalid addr", (addr != NULL), return ERR_ARG;);
+ LWIP_ERROR("netconn_getaddr: invalid port", (port != NULL), return ERR_ARG;);
+
+ API_MSG_VAR_ALLOC(msg);
+ API_MSG_VAR_REF(msg).conn = conn;
+ API_MSG_VAR_REF(msg).msg.ad.local = local;
+#if LWIP_MPU_COMPATIBLE
+ err = netconn_apimsg(lwip_netconn_do_getaddr, &API_MSG_VAR_REF(msg));
+ *addr = msg->msg.ad.ipaddr;
+ *port = msg->msg.ad.port;
+#else /* LWIP_MPU_COMPATIBLE */
+ msg.msg.ad.ipaddr = addr;
+ msg.msg.ad.port = port;
+ err = netconn_apimsg(lwip_netconn_do_getaddr, &msg);
+#endif /* LWIP_MPU_COMPATIBLE */
+ API_MSG_VAR_FREE(msg);
+
+ return err;
+}
+
+/**
+ * @ingroup netconn_common
+ * Bind a netconn to a specific local IP address and port.
+ * Binding one netconn twice might not always be checked correctly!
+ *
+ * @param conn the netconn to bind
+ * @param addr the local IP address to bind the netconn to
+ * (use IP4_ADDR_ANY/IP6_ADDR_ANY to bind to all addresses)
+ * @param port the local port to bind the netconn to (not used for RAW)
+ * @return ERR_OK if bound, any other err_t on failure
+ */
+err_t
+netconn_bind(struct netconn *conn, const ip_addr_t *addr, u16_t port)
+{
+ API_MSG_VAR_DECLARE(msg);
+ err_t err;
+
+ LWIP_ERROR("netconn_bind: invalid conn", (conn != NULL), return ERR_ARG;);
+
+#if LWIP_IPV4
+ /* Don't propagate NULL pointer (IP_ADDR_ANY alias) to subsequent functions */
+ if (addr == NULL) {
+ addr = IP4_ADDR_ANY;
+ }
+#endif /* LWIP_IPV4 */
+
+#if LWIP_IPV4 && LWIP_IPV6
+ /* "Socket API like" dual-stack support: If IP to bind to is IP6_ADDR_ANY,
+ * and NETCONN_FLAG_IPV6_V6ONLY is 0, use IP_ANY_TYPE to bind
+ */
+ if ((netconn_get_ipv6only(conn) == 0) &&
+ ip_addr_cmp(addr, IP6_ADDR_ANY)) {
+ addr = IP_ANY_TYPE;
+ }
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+
+ API_MSG_VAR_ALLOC(msg);
+ API_MSG_VAR_REF(msg).conn = conn;
+ API_MSG_VAR_REF(msg).msg.bc.ipaddr = API_MSG_VAR_REF(addr);
+ API_MSG_VAR_REF(msg).msg.bc.port = port;
+ err = netconn_apimsg(lwip_netconn_do_bind, &API_MSG_VAR_REF(msg));
+ API_MSG_VAR_FREE(msg);
+
+ return err;
+}
+
+/**
+ * @ingroup netconn_common
+ * Connect a netconn to a specific remote IP address and port.
+ *
+ * @param conn the netconn to connect
+ * @param addr the remote IP address to connect to
+ * @param port the remote port to connect to (no used for RAW)
+ * @return ERR_OK if connected, return value of tcp_/udp_/raw_connect otherwise
+ */
+err_t
+netconn_connect(struct netconn *conn, const ip_addr_t *addr, u16_t port)
+{
+ API_MSG_VAR_DECLARE(msg);
+ err_t err;
+
+ LWIP_ERROR("netconn_connect: invalid conn", (conn != NULL), return ERR_ARG;);
+
+#if LWIP_IPV4
+ /* Don't propagate NULL pointer (IP_ADDR_ANY alias) to subsequent functions */
+ if (addr == NULL) {
+ addr = IP4_ADDR_ANY;
+ }
+#endif /* LWIP_IPV4 */
+
+ API_MSG_VAR_ALLOC(msg);
+ API_MSG_VAR_REF(msg).conn = conn;
+ API_MSG_VAR_REF(msg).msg.bc.ipaddr = API_MSG_VAR_REF(addr);
+ API_MSG_VAR_REF(msg).msg.bc.port = port;
+ err = netconn_apimsg(lwip_netconn_do_connect, &API_MSG_VAR_REF(msg));
+ API_MSG_VAR_FREE(msg);
+
+ return err;
+}
+
+/**
+ * @ingroup netconn_udp
+ * Disconnect a netconn from its current peer (only valid for UDP netconns).
+ *
+ * @param conn the netconn to disconnect
+ * @return See @ref err_t
+ */
+err_t
+netconn_disconnect(struct netconn *conn)
+{
+ API_MSG_VAR_DECLARE(msg);
+ err_t err;
+
+ LWIP_ERROR("netconn_disconnect: invalid conn", (conn != NULL), return ERR_ARG;);
+
+ API_MSG_VAR_ALLOC(msg);
+ API_MSG_VAR_REF(msg).conn = conn;
+ err = netconn_apimsg(lwip_netconn_do_disconnect, &API_MSG_VAR_REF(msg));
+ API_MSG_VAR_FREE(msg);
+
+ return err;
+}
+
+/**
+ * @ingroup netconn_tcp
+ * Set a TCP netconn into listen mode
+ *
+ * @param conn the tcp netconn to set to listen mode
+ * @param backlog the listen backlog, only used if TCP_LISTEN_BACKLOG==1
+ * @return ERR_OK if the netconn was set to listen (UDP and RAW netconns
+ * don't return any error (yet?))
+ */
+err_t
+netconn_listen_with_backlog(struct netconn *conn, u8_t backlog)
+{
+#if LWIP_TCP
+ API_MSG_VAR_DECLARE(msg);
+ err_t err;
+
+ /* This does no harm. If TCP_LISTEN_BACKLOG is off, backlog is unused. */
+ LWIP_UNUSED_ARG(backlog);
+
+ LWIP_ERROR("netconn_listen: invalid conn", (conn != NULL), return ERR_ARG;);
+
+ API_MSG_VAR_ALLOC(msg);
+ API_MSG_VAR_REF(msg).conn = conn;
+#if TCP_LISTEN_BACKLOG
+ API_MSG_VAR_REF(msg).msg.lb.backlog = backlog;
+#endif /* TCP_LISTEN_BACKLOG */
+ err = netconn_apimsg(lwip_netconn_do_listen, &API_MSG_VAR_REF(msg));
+ API_MSG_VAR_FREE(msg);
+
+ return err;
+#else /* LWIP_TCP */
+ LWIP_UNUSED_ARG(conn);
+ LWIP_UNUSED_ARG(backlog);
+ return ERR_ARG;
+#endif /* LWIP_TCP */
+}
+
+/**
+ * @ingroup netconn_tcp
+ * Accept a new connection on a TCP listening netconn.
+ *
+ * @param conn the TCP listen netconn
+ * @param new_conn pointer where the new connection is stored
+ * @return ERR_OK if a new connection has been received or an error
+ * code otherwise
+ */
+err_t
+netconn_accept(struct netconn *conn, struct netconn **new_conn)
+{
+#if LWIP_TCP
+ void *accept_ptr;
+ struct netconn *newconn;
+#if TCP_LISTEN_BACKLOG
+ API_MSG_VAR_DECLARE(msg);
+#endif /* TCP_LISTEN_BACKLOG */
+
+ LWIP_ERROR("netconn_accept: invalid pointer", (new_conn != NULL), return ERR_ARG;);
+ *new_conn = NULL;
+ LWIP_ERROR("netconn_accept: invalid conn", (conn != NULL), return ERR_ARG;);
+
+ if (ERR_IS_FATAL(conn->last_err)) {
+ /* don't recv on fatal errors: this might block the application task
+ waiting on acceptmbox forever! */
+ return conn->last_err;
+ }
+ if (!sys_mbox_valid(&conn->acceptmbox)) {
+ return ERR_CLSD;
+ }
+
+#if TCP_LISTEN_BACKLOG
+ API_MSG_VAR_ALLOC(msg);
+#endif /* TCP_LISTEN_BACKLOG */
+
+#if LWIP_SO_RCVTIMEO
+ if (sys_arch_mbox_fetch(&conn->acceptmbox, &accept_ptr, conn->recv_timeout) == SYS_ARCH_TIMEOUT) {
+#if TCP_LISTEN_BACKLOG
+ API_MSG_VAR_FREE(msg);
+#endif /* TCP_LISTEN_BACKLOG */
+ return ERR_TIMEOUT;
+ }
+#else
+ sys_arch_mbox_fetch(&conn->acceptmbox, &accept_ptr, 0);
+#endif /* LWIP_SO_RCVTIMEO*/
+ newconn = (struct netconn *)accept_ptr;
+ /* Register event with callback */
+ API_EVENT(conn, NETCONN_EVT_RCVMINUS, 0);
+
+ if (accept_ptr == &netconn_aborted) {
+ /* a connection has been aborted: out of pcbs or out of netconns during accept */
+ /* @todo: set netconn error, but this would be fatal and thus block further accepts */
+#if TCP_LISTEN_BACKLOG
+ API_MSG_VAR_FREE(msg);
+#endif /* TCP_LISTEN_BACKLOG */
+ return ERR_ABRT;
+ }
+ if (newconn == NULL) {
+ /* connection has been aborted */
+ /* in this special case, we set the netconn error from application thread, as
+ on a ready-to-accept listening netconn, there should not be anything running
+ in tcpip_thread */
+ NETCONN_SET_SAFE_ERR(conn, ERR_CLSD);
+#if TCP_LISTEN_BACKLOG
+ API_MSG_VAR_FREE(msg);
+#endif /* TCP_LISTEN_BACKLOG */
+ return ERR_CLSD;
+ }
+#if TCP_LISTEN_BACKLOG
+ /* Let the stack know that we have accepted the connection. */
+ API_MSG_VAR_REF(msg).conn = newconn;
+ /* don't care for the return value of lwip_netconn_do_recv */
+ netconn_apimsg(lwip_netconn_do_accepted, &API_MSG_VAR_REF(msg));
+ API_MSG_VAR_FREE(msg);
+#endif /* TCP_LISTEN_BACKLOG */
+
+ *new_conn = newconn;
+ /* don't set conn->last_err: it's only ERR_OK, anyway */
+ return ERR_OK;
+#else /* LWIP_TCP */
+ LWIP_UNUSED_ARG(conn);
+ LWIP_UNUSED_ARG(new_conn);
+ return ERR_ARG;
+#endif /* LWIP_TCP */
+}
+
+/**
+ * @ingroup netconn_common
+ * Receive data: actual implementation that doesn't care whether pbuf or netbuf
+ * is received
+ *
+ * @param conn the netconn from which to receive data
+ * @param new_buf pointer where a new pbuf/netbuf is stored when received data
+ * @return ERR_OK if data has been received, an error code otherwise (timeout,
+ * memory error or another error)
+ */
+static err_t
+netconn_recv_data(struct netconn *conn, void **new_buf)
+{
+ void *buf = NULL;
+ u16_t len;
+#if LWIP_TCP
+ API_MSG_VAR_DECLARE(msg);
+#if LWIP_MPU_COMPATIBLE
+ msg = NULL;
+#endif
+#endif /* LWIP_TCP */
+
+ LWIP_ERROR("netconn_recv: invalid pointer", (new_buf != NULL), return ERR_ARG;);
+ *new_buf = NULL;
+ LWIP_ERROR("netconn_recv: invalid conn", (conn != NULL), return ERR_ARG;);
+#if LWIP_TCP
+#if (LWIP_UDP || LWIP_RAW)
+ if (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP)
+#endif /* (LWIP_UDP || LWIP_RAW) */
+ {
+ if (!sys_mbox_valid(&conn->recvmbox)) {
+ /* This happens when calling this function after receiving FIN */
+ return sys_mbox_valid(&conn->acceptmbox) ? ERR_CONN : ERR_CLSD;
+ }
+ }
+#endif /* LWIP_TCP */
+ LWIP_ERROR("netconn_recv: invalid recvmbox", sys_mbox_valid(&conn->recvmbox), return ERR_CONN;);
+
+ if (ERR_IS_FATAL(conn->last_err)) {
+ /* don't recv on fatal errors: this might block the application task
+ waiting on recvmbox forever! */
+ /* @todo: this does not allow us to fetch data that has been put into recvmbox
+ before the fatal error occurred - is that a problem? */
+ return conn->last_err;
+ }
+#if LWIP_TCP
+#if (LWIP_UDP || LWIP_RAW)
+ if (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP)
+#endif /* (LWIP_UDP || LWIP_RAW) */
+ {
+ API_MSG_VAR_ALLOC(msg);
+ }
+#endif /* LWIP_TCP */
+
+#if LWIP_SO_RCVTIMEO
+ if (sys_arch_mbox_fetch(&conn->recvmbox, &buf, conn->recv_timeout) == SYS_ARCH_TIMEOUT) {
+#if LWIP_TCP
+#if (LWIP_UDP || LWIP_RAW)
+ if (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP)
+#endif /* (LWIP_UDP || LWIP_RAW) */
+ {
+ API_MSG_VAR_FREE(msg);
+ }
+#endif /* LWIP_TCP */
+ return ERR_TIMEOUT;
+ }
+#else
+ sys_arch_mbox_fetch(&conn->recvmbox, &buf, 0);
+#endif /* LWIP_SO_RCVTIMEO*/
+
+#if LWIP_TCP
+#if (LWIP_UDP || LWIP_RAW)
+ if (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP)
+#endif /* (LWIP_UDP || LWIP_RAW) */
+ {
+ /* Let the stack know that we have taken the data. */
+ /* @todo: Speedup: Don't block and wait for the answer here
+ (to prevent multiple thread-switches). */
+ API_MSG_VAR_REF(msg).conn = conn;
+ if (buf != NULL) {
+ API_MSG_VAR_REF(msg).msg.r.len = ((struct pbuf *)buf)->tot_len;
+ } else {
+ API_MSG_VAR_REF(msg).msg.r.len = 1;
+ }
+
+ /* don't care for the return value of lwip_netconn_do_recv */
+ netconn_apimsg(lwip_netconn_do_recv, &API_MSG_VAR_REF(msg));
+ API_MSG_VAR_FREE(msg);
+
+ /* If we are closed, we indicate that we no longer wish to use the socket */
+ if (buf == NULL) {
+ API_EVENT(conn, NETCONN_EVT_RCVMINUS, 0);
+ if (conn->pcb.ip == NULL) {
+ /* race condition: RST during recv */
+ return conn->last_err == ERR_OK ? ERR_RST : conn->last_err;
+ }
+ /* RX side is closed, so deallocate the recvmbox */
+ netconn_close_shutdown(conn, NETCONN_SHUT_RD);
+ /* Don' store ERR_CLSD as conn->err since we are only half-closed */
+ return ERR_CLSD;
+ }
+ len = ((struct pbuf *)buf)->tot_len;
+ }
+#endif /* LWIP_TCP */
+#if LWIP_TCP && (LWIP_UDP || LWIP_RAW)
+ else
+#endif /* LWIP_TCP && (LWIP_UDP || LWIP_RAW) */
+#if (LWIP_UDP || LWIP_RAW)
+ {
+ LWIP_ASSERT("buf != NULL", buf != NULL);
+ len = netbuf_len((struct netbuf*)buf);
+ }
+#endif /* (LWIP_UDP || LWIP_RAW) */
+
+#if LWIP_SO_RCVBUF
+ SYS_ARCH_DEC(conn->recv_avail, len);
+#endif /* LWIP_SO_RCVBUF */
+ /* Register event with callback */
+ API_EVENT(conn, NETCONN_EVT_RCVMINUS, len);
+
+ LWIP_DEBUGF(API_LIB_DEBUG, ("netconn_recv_data: received %p, len=%"U16_F"\n", buf, len));
+
+ *new_buf = buf;
+ /* don't set conn->last_err: it's only ERR_OK, anyway */
+ return ERR_OK;
+}
+
+/**
+ * @ingroup netconn_tcp
+ * Receive data (in form of a pbuf) from a TCP netconn
+ *
+ * @param conn the netconn from which to receive data
+ * @param new_buf pointer where a new pbuf is stored when received data
+ * @return ERR_OK if data has been received, an error code otherwise (timeout,
+ * memory error or another error)
+ * ERR_ARG if conn is not a TCP netconn
+ */
+err_t
+netconn_recv_tcp_pbuf(struct netconn *conn, struct pbuf **new_buf)
+{
+ LWIP_ERROR("netconn_recv: invalid conn", (conn != NULL) &&
+ NETCONNTYPE_GROUP(netconn_type(conn)) == NETCONN_TCP, return ERR_ARG;);
+
+ return netconn_recv_data(conn, (void **)new_buf);
+}
+
+/**
+ * @ingroup netconn_common
+ * Receive data (in form of a netbuf containing a packet buffer) from a netconn
+ *
+ * @param conn the netconn from which to receive data
+ * @param new_buf pointer where a new netbuf is stored when received data
+ * @return ERR_OK if data has been received, an error code otherwise (timeout,
+ * memory error or another error)
+ */
+err_t
+netconn_recv(struct netconn *conn, struct netbuf **new_buf)
+{
+#if LWIP_TCP
+ struct netbuf *buf = NULL;
+ err_t err;
+#endif /* LWIP_TCP */
+
+ LWIP_ERROR("netconn_recv: invalid pointer", (new_buf != NULL), return ERR_ARG;);
+ *new_buf = NULL;
+ LWIP_ERROR("netconn_recv: invalid conn", (conn != NULL), return ERR_ARG;);
+
+#if LWIP_TCP
+#if (LWIP_UDP || LWIP_RAW)
+ if (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP)
+#endif /* (LWIP_UDP || LWIP_RAW) */
+ {
+ struct pbuf *p = NULL;
+ /* This is not a listening netconn, since recvmbox is set */
+
+ buf = (struct netbuf *)memp_malloc(MEMP_NETBUF);
+ if (buf == NULL) {
+ return ERR_MEM;
+ }
+
+ err = netconn_recv_data(conn, (void **)&p);
+ if (err != ERR_OK) {
+ memp_free(MEMP_NETBUF, buf);
+ return err;
+ }
+ LWIP_ASSERT("p != NULL", p != NULL);
+
+ buf->p = p;
+ buf->ptr = p;
+ buf->port = 0;
+ ip_addr_set_zero(&buf->addr);
+ *new_buf = buf;
+ /* don't set conn->last_err: it's only ERR_OK, anyway */
+ return ERR_OK;
+ }
+#endif /* LWIP_TCP */
+#if LWIP_TCP && (LWIP_UDP || LWIP_RAW)
+ else
+#endif /* LWIP_TCP && (LWIP_UDP || LWIP_RAW) */
+ {
+#if (LWIP_UDP || LWIP_RAW)
+ return netconn_recv_data(conn, (void **)new_buf);
+#endif /* (LWIP_UDP || LWIP_RAW) */
+ }
+}
+
+/**
+ * @ingroup netconn_udp
+ * Send data (in form of a netbuf) to a specific remote IP address and port.
+ * Only to be used for UDP and RAW netconns (not TCP).
+ *
+ * @param conn the netconn over which to send data
+ * @param buf a netbuf containing the data to send
+ * @param addr the remote IP address to which to send the data
+ * @param port the remote port to which to send the data
+ * @return ERR_OK if data was sent, any other err_t on error
+ */
+err_t
+netconn_sendto(struct netconn *conn, struct netbuf *buf, const ip_addr_t *addr, u16_t port)
+{
+ if (buf != NULL) {
+ ip_addr_set(&buf->addr, addr);
+ buf->port = port;
+ return netconn_send(conn, buf);
+ }
+ return ERR_VAL;
+}
+
+/**
+ * @ingroup netconn_udp
+ * Send data over a UDP or RAW netconn (that is already connected).
+ *
+ * @param conn the UDP or RAW netconn over which to send data
+ * @param buf a netbuf containing the data to send
+ * @return ERR_OK if data was sent, any other err_t on error
+ */
+err_t
+netconn_send(struct netconn *conn, struct netbuf *buf)
+{
+ API_MSG_VAR_DECLARE(msg);
+ err_t err;
+
+ LWIP_ERROR("netconn_send: invalid conn", (conn != NULL), return ERR_ARG;);
+
+ LWIP_DEBUGF(API_LIB_DEBUG, ("netconn_send: sending %"U16_F" bytes\n", buf->p->tot_len));
+
+ API_MSG_VAR_ALLOC(msg);
+ API_MSG_VAR_REF(msg).conn = conn;
+ API_MSG_VAR_REF(msg).msg.b = buf;
+ err = netconn_apimsg(lwip_netconn_do_send, &API_MSG_VAR_REF(msg));
+ API_MSG_VAR_FREE(msg);
+
+ return err;
+}
+
+/**
+ * @ingroup netconn_tcp
+ * Send data over a TCP netconn.
+ *
+ * @param conn the TCP netconn over which to send data
+ * @param dataptr pointer to the application buffer that contains the data to send
+ * @param size size of the application data to send
+ * @param apiflags combination of following flags :
+ * - NETCONN_COPY: data will be copied into memory belonging to the stack
+ * - NETCONN_MORE: for TCP connection, PSH flag will be set on last segment sent
+ * - NETCONN_DONTBLOCK: only write the data if all data 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_partly(struct netconn *conn, const void *dataptr, size_t size,
+ u8_t apiflags, size_t *bytes_written)
+{
+ API_MSG_VAR_DECLARE(msg);
+ err_t err;
+ u8_t dontblock;
+
+ LWIP_ERROR("netconn_write: invalid conn", (conn != NULL), return ERR_ARG;);
+ LWIP_ERROR("netconn_write: invalid conn->type", (NETCONNTYPE_GROUP(conn->type)== NETCONN_TCP), return ERR_VAL;);
+ if (size == 0) {
+ return ERR_OK;
+ }
+ dontblock = netconn_is_nonblocking(conn) || (apiflags & NETCONN_DONTBLOCK);
+#if LWIP_SO_SNDTIMEO
+ if (conn->send_timeout != 0) {
+ dontblock = 1;
+ }
+#endif /* LWIP_SO_SNDTIMEO */
+ 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;
+ }
+
+ API_MSG_VAR_ALLOC(msg);
+ /* non-blocking write sends as much */
+ API_MSG_VAR_REF(msg).conn = conn;
+ API_MSG_VAR_REF(msg).msg.w.dataptr = dataptr;
+ API_MSG_VAR_REF(msg).msg.w.apiflags = apiflags;
+ API_MSG_VAR_REF(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 */
+ API_MSG_VAR_REF(msg).msg.w.time_started = sys_now();
+ } else {
+ API_MSG_VAR_REF(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 = netconn_apimsg(lwip_netconn_do_write, &API_MSG_VAR_REF(msg));
+ if ((err == ERR_OK) && (bytes_written != NULL)) {
+ if (dontblock) {
+ /* nonblocking write: maybe the data has been sent partly */
+ *bytes_written = API_MSG_VAR_REF(msg).msg.w.len;
+ } else {
+ /* blocking call succeeded: all data has been sent if it */
+ *bytes_written = size;
+ }
+ }
+ API_MSG_VAR_FREE(msg);
+
+ return err;
+}
+
+/**
+ * @ingroup netconn_tcp
+ * Close or shutdown a TCP netconn (doesn't delete it).
+ *
+ * @param conn the TCP netconn to close or shutdown
+ * @param how fully close or only shutdown one side?
+ * @return ERR_OK if the netconn was closed, any other err_t on error
+ */
+static err_t
+netconn_close_shutdown(struct netconn *conn, u8_t how)
+{
+ API_MSG_VAR_DECLARE(msg);
+ err_t err;
+ LWIP_UNUSED_ARG(how);
+
+ LWIP_ERROR("netconn_close: invalid conn", (conn != NULL), return ERR_ARG;);
+
+ API_MSG_VAR_ALLOC(msg);
+ API_MSG_VAR_REF(msg).conn = conn;
+#if LWIP_TCP
+ /* shutting down both ends is the same as closing */
+ API_MSG_VAR_REF(msg).msg.sd.shut = how;
+#if LWIP_SO_SNDTIMEO || LWIP_SO_LINGER
+ /* get the time we started, which is later compared to
+ sys_now() + conn->send_timeout */
+ API_MSG_VAR_REF(msg).msg.sd.time_started = sys_now();
+#else /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */
+ API_MSG_VAR_REF(msg).msg.sd.polls_left =
+ ((LWIP_TCP_CLOSE_TIMEOUT_MS_DEFAULT + TCP_SLOW_INTERVAL - 1) / TCP_SLOW_INTERVAL) + 1;
+#endif /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */
+#endif /* LWIP_TCP */
+ err = netconn_apimsg(lwip_netconn_do_close, &API_MSG_VAR_REF(msg));
+ API_MSG_VAR_FREE(msg);
+
+ return err;
+}
+
+/**
+ * @ingroup netconn_tcp
+ * Close a TCP netconn (doesn't delete it).
+ *
+ * @param conn the TCP netconn to close
+ * @return ERR_OK if the netconn was closed, any other err_t on error
+ */
+err_t
+netconn_close(struct netconn *conn)
+{
+ /* shutting down both ends is the same as closing */
+ return netconn_close_shutdown(conn, NETCONN_SHUT_RDWR);
+}
+
+/**
+ * @ingroup netconn_tcp
+ * Shut down one or both sides of a TCP netconn (doesn't delete it).
+ *
+ * @param conn the TCP netconn to shut down
+ * @param shut_rx shut down the RX side (no more read possible after this)
+ * @param shut_tx shut down the TX side (no more write possible after this)
+ * @return ERR_OK if the netconn was closed, any other err_t on error
+ */
+err_t
+netconn_shutdown(struct netconn *conn, u8_t shut_rx, u8_t shut_tx)
+{
+ return netconn_close_shutdown(conn, (shut_rx ? NETCONN_SHUT_RD : 0) | (shut_tx ? NETCONN_SHUT_WR : 0));
+}
+
+#if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD)
+/**
+ * @ingroup netconn_udp
+ * Join multicast groups for UDP netconns.
+ *
+ * @param conn the UDP netconn for which to change multicast addresses
+ * @param multiaddr IP address of the multicast group to join or leave
+ * @param netif_addr the IP address of the network interface on which to send
+ * the igmp message
+ * @param join_or_leave flag whether to send a join- or leave-message
+ * @return ERR_OK if the action was taken, any err_t on error
+ */
+err_t
+netconn_join_leave_group(struct netconn *conn,
+ const ip_addr_t *multiaddr,
+ const ip_addr_t *netif_addr,
+ enum netconn_igmp join_or_leave)
+{
+ API_MSG_VAR_DECLARE(msg);
+ err_t err;
+
+ LWIP_ERROR("netconn_join_leave_group: invalid conn", (conn != NULL), return ERR_ARG;);
+
+ API_MSG_VAR_ALLOC(msg);
+
+#if LWIP_IPV4
+ /* Don't propagate NULL pointer (IP_ADDR_ANY alias) to subsequent functions */
+ if (multiaddr == NULL) {
+ multiaddr = IP4_ADDR_ANY;
+ }
+ if (netif_addr == NULL) {
+ netif_addr = IP4_ADDR_ANY;
+ }
+#endif /* LWIP_IPV4 */
+
+ API_MSG_VAR_REF(msg).conn = conn;
+ API_MSG_VAR_REF(msg).msg.jl.multiaddr = API_MSG_VAR_REF(multiaddr);
+ API_MSG_VAR_REF(msg).msg.jl.netif_addr = API_MSG_VAR_REF(netif_addr);
+ API_MSG_VAR_REF(msg).msg.jl.join_or_leave = join_or_leave;
+ err = netconn_apimsg(lwip_netconn_do_join_leave_group, &API_MSG_VAR_REF(msg));
+ API_MSG_VAR_FREE(msg);
+
+ return err;
+}
+#endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */
+
+#if LWIP_DNS
+/**
+ * @ingroup netconn_common
+ * Execute a DNS query, only one IP address is returned
+ *
+ * @param name a string representation of the DNS host name to query
+ * @param addr a preallocated ip_addr_t where to store the resolved IP address
+ * @param dns_addrtype IP address type (IPv4 / IPv6)
+ * @return ERR_OK: resolving succeeded
+ * ERR_MEM: memory error, try again later
+ * ERR_ARG: dns client not initialized or invalid hostname
+ * ERR_VAL: dns server response was invalid
+ */
+#if LWIP_IPV4 && LWIP_IPV6
+err_t
+netconn_gethostbyname_addrtype(const char *name, ip_addr_t *addr, u8_t dns_addrtype)
+#else
+err_t
+netconn_gethostbyname(const char *name, ip_addr_t *addr)
+#endif
+{
+ API_VAR_DECLARE(struct dns_api_msg, msg);
+#if !LWIP_MPU_COMPATIBLE
+ sys_sem_t sem;
+#endif /* LWIP_MPU_COMPATIBLE */
+ err_t err;
+ err_t cberr;
+
+ LWIP_ERROR("netconn_gethostbyname: invalid name", (name != NULL), return ERR_ARG;);
+ LWIP_ERROR("netconn_gethostbyname: invalid addr", (addr != NULL), return ERR_ARG;);
+#if LWIP_MPU_COMPATIBLE
+ if (strlen(name) >= DNS_MAX_NAME_LENGTH) {
+ return ERR_ARG;
+ }
+#endif
+
+ API_VAR_ALLOC(struct dns_api_msg, MEMP_DNS_API_MSG, msg, ERR_MEM);
+#if LWIP_MPU_COMPATIBLE
+ strncpy(API_VAR_REF(msg).name, name, DNS_MAX_NAME_LENGTH-1);
+ API_VAR_REF(msg).name[DNS_MAX_NAME_LENGTH-1] = 0;
+#else /* LWIP_MPU_COMPATIBLE */
+ msg.err = &err;
+ msg.sem = &sem;
+ API_VAR_REF(msg).addr = API_VAR_REF(addr);
+ API_VAR_REF(msg).name = name;
+#endif /* LWIP_MPU_COMPATIBLE */
+#if LWIP_IPV4 && LWIP_IPV6
+ API_VAR_REF(msg).dns_addrtype = dns_addrtype;
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+#if LWIP_NETCONN_SEM_PER_THREAD
+ API_VAR_REF(msg).sem = LWIP_NETCONN_THREAD_SEM_GET();
+#else /* LWIP_NETCONN_SEM_PER_THREAD*/
+ err = sys_sem_new(API_EXPR_REF(API_VAR_REF(msg).sem), 0);
+ if (err != ERR_OK) {
+ API_VAR_FREE(MEMP_DNS_API_MSG, msg);
+ return err;
+ }
+#endif /* LWIP_NETCONN_SEM_PER_THREAD */
+
+ cberr = tcpip_callback(lwip_netconn_do_gethostbyname, &API_VAR_REF(msg));
+ if (cberr != ERR_OK) {
+#if !LWIP_NETCONN_SEM_PER_THREAD
+ sys_sem_free(API_EXPR_REF(API_VAR_REF(msg).sem));
+#endif /* !LWIP_NETCONN_SEM_PER_THREAD */
+ API_VAR_FREE(MEMP_DNS_API_MSG, msg);
+ return cberr;
+ }
+ sys_sem_wait(API_EXPR_REF_SEM(API_VAR_REF(msg).sem));
+#if !LWIP_NETCONN_SEM_PER_THREAD
+ sys_sem_free(API_EXPR_REF(API_VAR_REF(msg).sem));
+#endif /* !LWIP_NETCONN_SEM_PER_THREAD */
+
+#if LWIP_MPU_COMPATIBLE
+ *addr = msg->addr;
+ err = msg->err;
+#endif /* LWIP_MPU_COMPATIBLE */
+
+ API_VAR_FREE(MEMP_DNS_API_MSG, msg);
+ return err;
+}
+#endif /* LWIP_DNS*/
+
+#if LWIP_NETCONN_SEM_PER_THREAD
+void
+netconn_thread_init(void)
+{
+ sys_sem_t *sem = LWIP_NETCONN_THREAD_SEM_GET();
+ if ((sem == NULL) || !sys_sem_valid(sem)) {
+ /* call alloc only once */
+ LWIP_NETCONN_THREAD_SEM_ALLOC();
+ LWIP_ASSERT("LWIP_NETCONN_THREAD_SEM_ALLOC() failed", sys_sem_valid(LWIP_NETCONN_THREAD_SEM_GET()));
+ }
+}
+
+void
+netconn_thread_cleanup(void)
+{
+ sys_sem_t *sem = LWIP_NETCONN_THREAD_SEM_GET();
+ if ((sem != NULL) && sys_sem_valid(sem)) {
+ /* call free only once */
+ LWIP_NETCONN_THREAD_SEM_FREE();
+ }
+}
+#endif /* LWIP_NETCONN_SEM_PER_THREAD */
+
+#endif /* LWIP_NETCONN */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/api/api_msg.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/api/api_msg.c
new file mode 100644
index 0000000..dd99c1e
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/api/api_msg.c
@@ -0,0 +1,1947 @@
+/**
+ * @file
+ * Sequential API Internal module
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/priv/api_msg.h"
+
+#include "lwip/ip.h"
+#include "lwip/ip_addr.h"
+#include "lwip/udp.h"
+#include "lwip/tcp.h"
+#include "lwip/raw.h"
+
+#include "lwip/memp.h"
+#include "lwip/igmp.h"
+#include "lwip/dns.h"
+#include "lwip/mld6.h"
+#include "lwip/priv/tcpip_priv.h"
+
+#include <string.h>
+
+/* netconns are polled once per second (e.g. continue write on memory error) */
+#define NETCONN_TCP_POLL_INTERVAL 2
+
+#define SET_NONBLOCKING_CONNECT(conn, val) do { if (val) { \
+ (conn)->flags |= NETCONN_FLAG_IN_NONBLOCKING_CONNECT; \
+} else { \
+ (conn)->flags &= ~ NETCONN_FLAG_IN_NONBLOCKING_CONNECT; }} while(0)
+#define IN_NONBLOCKING_CONNECT(conn) (((conn)->flags & NETCONN_FLAG_IN_NONBLOCKING_CONNECT) != 0)
+
+/* forward declarations */
+#if LWIP_TCP
+#if LWIP_TCPIP_CORE_LOCKING
+#define WRITE_DELAYED , 1
+#define WRITE_DELAYED_PARAM , u8_t delayed
+#else /* LWIP_TCPIP_CORE_LOCKING */
+#define WRITE_DELAYED
+#define WRITE_DELAYED_PARAM
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+static err_t lwip_netconn_do_writemore(struct netconn *conn WRITE_DELAYED_PARAM);
+static err_t lwip_netconn_do_close_internal(struct netconn *conn WRITE_DELAYED_PARAM);
+#endif
+
+#if LWIP_TCPIP_CORE_LOCKING
+#define TCPIP_APIMSG_ACK(m) NETCONN_SET_SAFE_ERR((m)->conn, (m)->err)
+#else /* LWIP_TCPIP_CORE_LOCKING */
+#define TCPIP_APIMSG_ACK(m) do { NETCONN_SET_SAFE_ERR((m)->conn, (m)->err); sys_sem_signal(LWIP_API_MSG_SEM(m)); } while(0)
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+
+#if LWIP_TCP
+u8_t netconn_aborted;
+#endif /* LWIP_TCP */
+
+#if LWIP_RAW
+/**
+ * Receive callback function for RAW netconns.
+ * Doesn't 'eat' the packet, only copies it and sends it to
+ * conn->recvmbox
+ *
+ * @see raw.h (struct raw_pcb.recv) for parameters and return value
+ */
+static u8_t
+recv_raw(void *arg, struct raw_pcb *pcb, struct pbuf *p,
+ const ip_addr_t *addr)
+{
+ struct pbuf *q;
+ struct netbuf *buf;
+ struct netconn *conn;
+
+ LWIP_UNUSED_ARG(addr);
+ conn = (struct netconn *)arg;
+
+ if ((conn != NULL) && sys_mbox_valid(&conn->recvmbox)) {
+#if LWIP_SO_RCVBUF
+ int recv_avail;
+ SYS_ARCH_GET(conn->recv_avail, recv_avail);
+ if ((recv_avail + (int)(p->tot_len)) > conn->recv_bufsize) {
+ return 0;
+ }
+#endif /* LWIP_SO_RCVBUF */
+ /* copy the whole packet into new pbufs */
+ q = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM);
+ if (q != NULL) {
+ if (pbuf_copy(q, p) != ERR_OK) {
+ pbuf_free(q);
+ q = NULL;
+ }
+ }
+
+ if (q != NULL) {
+ u16_t len;
+ buf = (struct netbuf *)memp_malloc(MEMP_NETBUF);
+ if (buf == NULL) {
+ pbuf_free(q);
+ return 0;
+ }
+
+ buf->p = q;
+ buf->ptr = q;
+ ip_addr_copy(buf->addr, *ip_current_src_addr());
+ buf->port = pcb->protocol;
+
+ len = q->tot_len;
+ if (sys_mbox_trypost(&conn->recvmbox, buf) != ERR_OK) {
+ netbuf_delete(buf);
+ return 0;
+ } else {
+#if LWIP_SO_RCVBUF
+ SYS_ARCH_INC(conn->recv_avail, len);
+#endif /* LWIP_SO_RCVBUF */
+ /* Register event with callback */
+ API_EVENT(conn, NETCONN_EVT_RCVPLUS, len);
+ }
+ }
+ }
+
+ return 0; /* do not eat the packet */
+}
+#endif /* LWIP_RAW*/
+
+#if LWIP_UDP
+/**
+ * Receive callback function for UDP netconns.
+ * Posts the packet to conn->recvmbox or deletes it on memory error.
+ *
+ * @see udp.h (struct udp_pcb.recv) for parameters
+ */
+static void
+recv_udp(void *arg, struct udp_pcb *pcb, struct pbuf *p,
+ const ip_addr_t *addr, u16_t port)
+{
+ struct netbuf *buf;
+ struct netconn *conn;
+ u16_t len;
+#if LWIP_SO_RCVBUF
+ int recv_avail;
+#endif /* LWIP_SO_RCVBUF */
+
+ LWIP_UNUSED_ARG(pcb); /* only used for asserts... */
+ LWIP_ASSERT("recv_udp must have a pcb argument", pcb != NULL);
+ LWIP_ASSERT("recv_udp must have an argument", arg != NULL);
+ conn = (struct netconn *)arg;
+ LWIP_ASSERT("recv_udp: recv for wrong pcb!", conn->pcb.udp == pcb);
+
+#if LWIP_SO_RCVBUF
+ SYS_ARCH_GET(conn->recv_avail, recv_avail);
+ if ((conn == NULL) || !sys_mbox_valid(&conn->recvmbox) ||
+ ((recv_avail + (int)(p->tot_len)) > conn->recv_bufsize)) {
+#else /* LWIP_SO_RCVBUF */
+ if ((conn == NULL) || !sys_mbox_valid(&conn->recvmbox)) {
+#endif /* LWIP_SO_RCVBUF */
+ pbuf_free(p);
+ return;
+ }
+
+ buf = (struct netbuf *)memp_malloc(MEMP_NETBUF);
+ if (buf == NULL) {
+ pbuf_free(p);
+ return;
+ } else {
+ buf->p = p;
+ buf->ptr = p;
+ ip_addr_set(&buf->addr, addr);
+ buf->port = port;
+#if LWIP_NETBUF_RECVINFO
+ {
+ /* get the UDP header - always in the first pbuf, ensured by udp_input */
+ const struct udp_hdr* udphdr = (const struct udp_hdr*)ip_next_header_ptr();
+#if LWIP_CHECKSUM_ON_COPY
+ buf->flags = NETBUF_FLAG_DESTADDR;
+#endif /* LWIP_CHECKSUM_ON_COPY */
+ ip_addr_set(&buf->toaddr, ip_current_dest_addr());
+ buf->toport_chksum = udphdr->dest;
+ }
+#endif /* LWIP_NETBUF_RECVINFO */
+ }
+
+ len = p->tot_len;
+ if (sys_mbox_trypost(&conn->recvmbox, buf) != ERR_OK) {
+ netbuf_delete(buf);
+ return;
+ } else {
+#if LWIP_SO_RCVBUF
+ SYS_ARCH_INC(conn->recv_avail, len);
+#endif /* LWIP_SO_RCVBUF */
+ /* Register event with callback */
+ API_EVENT(conn, NETCONN_EVT_RCVPLUS, len);
+ }
+}
+#endif /* LWIP_UDP */
+
+#if LWIP_TCP
+/**
+ * Receive callback function for TCP netconns.
+ * Posts the packet to conn->recvmbox, but doesn't delete it on errors.
+ *
+ * @see tcp.h (struct tcp_pcb.recv) for parameters and return value
+ */
+static err_t
+recv_tcp(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
+{
+ struct netconn *conn;
+ u16_t len;
+
+ LWIP_UNUSED_ARG(pcb);
+ LWIP_ASSERT("recv_tcp must have a pcb argument", pcb != NULL);
+ LWIP_ASSERT("recv_tcp must have an argument", arg != NULL);
+ conn = (struct netconn *)arg;
+
+ if (conn == NULL) {
+ return ERR_VAL;
+ }
+ LWIP_ASSERT("recv_tcp: recv for wrong pcb!", conn->pcb.tcp == pcb);
+
+ if (!sys_mbox_valid(&conn->recvmbox)) {
+ /* recvmbox already deleted */
+ if (p != NULL) {
+ tcp_recved(pcb, p->tot_len);
+ pbuf_free(p);
+ }
+ return ERR_OK;
+ }
+ /* Unlike for UDP or RAW pcbs, don't check for available space
+ using recv_avail since that could break the connection
+ (data is already ACKed) */
+
+ /* don't overwrite fatal errors! */
+ if (err != ERR_OK) {
+ NETCONN_SET_SAFE_ERR(conn, err);
+ }
+
+ if (p != NULL) {
+ len = p->tot_len;
+ } else {
+ len = 0;
+ }
+
+ if (sys_mbox_trypost(&conn->recvmbox, p) != ERR_OK) {
+ /* don't deallocate p: it is presented to us later again from tcp_fasttmr! */
+ return ERR_MEM;
+ } else {
+#if LWIP_SO_RCVBUF
+ SYS_ARCH_INC(conn->recv_avail, len);
+#endif /* LWIP_SO_RCVBUF */
+ /* Register event with callback */
+ API_EVENT(conn, NETCONN_EVT_RCVPLUS, len);
+ }
+
+ return ERR_OK;
+}
+
+/**
+ * Poll callback function for TCP netconns.
+ * Wakes up an application thread that waits for a connection to close
+ * or data to be sent. The application thread then takes the
+ * appropriate action to go on.
+ *
+ * Signals the conn->sem.
+ * netconn_close waits for conn->sem if closing failed.
+ *
+ * @see tcp.h (struct tcp_pcb.poll) for parameters and return value
+ */
+static err_t
+poll_tcp(void *arg, struct tcp_pcb *pcb)
+{
+ struct netconn *conn = (struct netconn *)arg;
+
+ LWIP_UNUSED_ARG(pcb);
+ LWIP_ASSERT("conn != NULL", (conn != NULL));
+
+ if (conn->state == NETCONN_WRITE) {
+ lwip_netconn_do_writemore(conn WRITE_DELAYED);
+ } else if (conn->state == NETCONN_CLOSE) {
+#if !LWIP_SO_SNDTIMEO && !LWIP_SO_LINGER
+ if (conn->current_msg && conn->current_msg->msg.sd.polls_left) {
+ conn->current_msg->msg.sd.polls_left--;
+ }
+#endif /* !LWIP_SO_SNDTIMEO && !LWIP_SO_LINGER */
+ lwip_netconn_do_close_internal(conn WRITE_DELAYED);
+ }
+ /* @todo: implement connect timeout here? */
+
+ /* Did a nonblocking write fail before? Then check available write-space. */
+ if (conn->flags & NETCONN_FLAG_CHECK_WRITESPACE) {
+ /* If the queued byte- or pbuf-count drops below the configured low-water limit,
+ let select mark this pcb as writable again. */
+ if ((conn->pcb.tcp != NULL) && (tcp_sndbuf(conn->pcb.tcp) > TCP_SNDLOWAT) &&
+ (tcp_sndqueuelen(conn->pcb.tcp) < TCP_SNDQUEUELOWAT)) {
+ conn->flags &= ~NETCONN_FLAG_CHECK_WRITESPACE;
+ API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0);
+ }
+ }
+
+ return ERR_OK;
+}
+
+/**
+ * Sent callback function for TCP netconns.
+ * Signals the conn->sem and calls API_EVENT.
+ * netconn_write waits for conn->sem if send buffer is low.
+ *
+ * @see tcp.h (struct tcp_pcb.sent) for parameters and return value
+ */
+static err_t
+sent_tcp(void *arg, struct tcp_pcb *pcb, u16_t len)
+{
+ struct netconn *conn = (struct netconn *)arg;
+
+ LWIP_UNUSED_ARG(pcb);
+ LWIP_ASSERT("conn != NULL", (conn != NULL));
+
+ if (conn) {
+ if (conn->state == NETCONN_WRITE) {
+ lwip_netconn_do_writemore(conn WRITE_DELAYED);
+ } else if (conn->state == NETCONN_CLOSE) {
+ lwip_netconn_do_close_internal(conn WRITE_DELAYED);
+ }
+
+ /* If the queued byte- or pbuf-count drops below the configured low-water limit,
+ let select mark this pcb as writable again. */
+ if ((conn->pcb.tcp != NULL) && (tcp_sndbuf(conn->pcb.tcp) > TCP_SNDLOWAT) &&
+ (tcp_sndqueuelen(conn->pcb.tcp) < TCP_SNDQUEUELOWAT)) {
+ conn->flags &= ~NETCONN_FLAG_CHECK_WRITESPACE;
+ API_EVENT(conn, NETCONN_EVT_SENDPLUS, len);
+ }
+ }
+
+ return ERR_OK;
+}
+
+/**
+ * Error callback function for TCP netconns.
+ * Signals conn->sem, posts to all conn mboxes and calls API_EVENT.
+ * The application thread has then to decide what to do.
+ *
+ * @see tcp.h (struct tcp_pcb.err) for parameters
+ */
+static void
+err_tcp(void *arg, err_t err)
+{
+ struct netconn *conn;
+ enum netconn_state old_state;
+
+ conn = (struct netconn *)arg;
+ LWIP_ASSERT("conn != NULL", (conn != NULL));
+
+ conn->pcb.tcp = NULL;
+
+ /* reset conn->state now before waking up other threads */
+ old_state = conn->state;
+ conn->state = NETCONN_NONE;
+
+ if (old_state == NETCONN_CLOSE) {
+ /* RST during close: let close return success & dealloc the netconn */
+ err = ERR_OK;
+ NETCONN_SET_SAFE_ERR(conn, ERR_OK);
+ } else {
+ /* no check since this is always fatal! */
+ SYS_ARCH_SET(conn->last_err, err);
+ }
+
+ /* @todo: the type of NETCONN_EVT created should depend on 'old_state' */
+
+ /* Notify the user layer about a connection error. Used to signal select. */
+ API_EVENT(conn, NETCONN_EVT_ERROR, 0);
+ /* Try to release selects pending on 'read' or 'write', too.
+ They will get an error if they actually try to read or write. */
+ API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0);
+ API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0);
+
+ /* pass NULL-message to recvmbox to wake up pending recv */
+ if (sys_mbox_valid(&conn->recvmbox)) {
+ /* use trypost to prevent deadlock */
+ sys_mbox_trypost(&conn->recvmbox, NULL);
+ }
+ /* pass NULL-message to acceptmbox to wake up pending accept */
+ if (sys_mbox_valid(&conn->acceptmbox)) {
+ /* use trypost to preven deadlock */
+ sys_mbox_trypost(&conn->acceptmbox, NULL);
+ }
+
+ if ((old_state == NETCONN_WRITE) || (old_state == NETCONN_CLOSE) ||
+ (old_state == NETCONN_CONNECT)) {
+ /* calling lwip_netconn_do_writemore/lwip_netconn_do_close_internal is not necessary
+ since the pcb has already been deleted! */
+ int was_nonblocking_connect = IN_NONBLOCKING_CONNECT(conn);
+ SET_NONBLOCKING_CONNECT(conn, 0);
+
+ if (!was_nonblocking_connect) {
+ sys_sem_t* op_completed_sem;
+ /* set error return code */
+ LWIP_ASSERT("conn->current_msg != NULL", conn->current_msg != NULL);
+ conn->current_msg->err = err;
+ op_completed_sem = LWIP_API_MSG_SEM(conn->current_msg);
+ LWIP_ASSERT("inavlid op_completed_sem", sys_sem_valid(op_completed_sem));
+ conn->current_msg = NULL;
+ /* wake up the waiting task */
+ NETCONN_SET_SAFE_ERR(conn, err);
+ sys_sem_signal(op_completed_sem);
+ }
+ } else {
+ LWIP_ASSERT("conn->current_msg == NULL", conn->current_msg == NULL);
+ }
+}
+
+/**
+ * Setup a tcp_pcb with the correct callback function pointers
+ * and their arguments.
+ *
+ * @param conn the TCP netconn to setup
+ */
+static void
+setup_tcp(struct netconn *conn)
+{
+ struct tcp_pcb *pcb;
+
+ pcb = conn->pcb.tcp;
+ tcp_arg(pcb, conn);
+ tcp_recv(pcb, recv_tcp);
+ tcp_sent(pcb, sent_tcp);
+ tcp_poll(pcb, poll_tcp, NETCONN_TCP_POLL_INTERVAL);
+ tcp_err(pcb, err_tcp);
+}
+
+/**
+ * Accept callback function for TCP netconns.
+ * Allocates a new netconn and posts that to conn->acceptmbox.
+ *
+ * @see tcp.h (struct tcp_pcb_listen.accept) for parameters and return value
+ */
+static err_t
+accept_function(void *arg, struct tcp_pcb *newpcb, err_t err)
+{
+ struct netconn *newconn;
+ struct netconn *conn = (struct netconn *)arg;
+
+ LWIP_DEBUGF(API_MSG_DEBUG, ("accept_function: newpcb->tate: %s\n", tcp_debug_state_str(newpcb->state)));
+
+ if (conn == NULL) {
+ return ERR_VAL;
+ }
+ if (!sys_mbox_valid(&conn->acceptmbox)) {
+ LWIP_DEBUGF(API_MSG_DEBUG, ("accept_function: acceptmbox already deleted\n"));
+ return ERR_VAL;
+ }
+
+ if (newpcb == NULL) {
+ /* out-of-pcbs during connect: pass on this error to the application */
+ if (sys_mbox_trypost(&conn->acceptmbox, &netconn_aborted) == ERR_OK) {
+ /* Register event with callback */
+ API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0);
+ }
+ return ERR_VAL;
+ }
+
+ /* We have to set the callback here even though
+ * the new socket is unknown. newconn->socket is marked as -1. */
+ newconn = netconn_alloc(conn->type, conn->callback);
+ if (newconn == NULL) {
+ /* outof netconns: pass on this error to the application */
+ if (sys_mbox_trypost(&conn->acceptmbox, &netconn_aborted) == ERR_OK) {
+ /* Register event with callback */
+ API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0);
+ }
+ return ERR_MEM;
+ }
+ newconn->pcb.tcp = newpcb;
+ setup_tcp(newconn);
+ /* no protection: when creating the pcb, the netconn is not yet known
+ to the application thread */
+ newconn->last_err = err;
+
+ /* handle backlog counter */
+ tcp_backlog_delayed(newpcb);
+
+ 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, 0);
+ tcp_err(pcb, NULL);
+ /* remove reference from to the pcb from this netconn */
+ newconn->pcb.tcp = NULL;
+ /* no need to drain since we know the recvmbox is empty. */
+ sys_mbox_free(&newconn->recvmbox);
+ sys_mbox_set_invalid(&newconn->recvmbox);
+ netconn_free(newconn);
+ return ERR_MEM;
+ } else {
+ /* Register event with callback */
+ API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0);
+ }
+
+ return ERR_OK;
+}
+#endif /* LWIP_TCP */
+
+/**
+ * Create a new pcb of a specific type.
+ * Called from lwip_netconn_do_newconn().
+ *
+ * @param msg the api_msg_msg describing the connection type
+ */
+static void
+pcb_new(struct api_msg *msg)
+{
+ enum lwip_ip_addr_type iptype = IPADDR_TYPE_V4;
+
+ LWIP_ASSERT("pcb_new: pcb already allocated", msg->conn->pcb.tcp == NULL);
+
+#if LWIP_IPV6 && LWIP_IPV4
+ /* IPv6: Dual-stack by default, unless netconn_set_ipv6only() is called */
+ if(NETCONNTYPE_ISIPV6(netconn_type(msg->conn))) {
+ iptype = IPADDR_TYPE_ANY;
+ }
+#endif
+
+ /* Allocate a PCB for this connection */
+ switch(NETCONNTYPE_GROUP(msg->conn->type)) {
+#if LWIP_RAW
+ case NETCONN_RAW:
+ msg->conn->pcb.raw = raw_new_ip_type(iptype, msg->msg.n.proto);
+ if (msg->conn->pcb.raw != NULL) {
+#if LWIP_IPV6
+ /* ICMPv6 packets should always have checksum calculated by the stack as per RFC 3542 chapter 3.1 */
+ if (NETCONNTYPE_ISIPV6(msg->conn->type) && msg->conn->pcb.raw->protocol == IP6_NEXTH_ICMP6) {
+ msg->conn->pcb.raw->chksum_reqd = 1;
+ msg->conn->pcb.raw->chksum_offset = 2;
+ }
+#endif /* LWIP_IPV6 */
+ raw_recv(msg->conn->pcb.raw, recv_raw, msg->conn);
+ }
+ break;
+#endif /* LWIP_RAW */
+#if LWIP_UDP
+ case NETCONN_UDP:
+ msg->conn->pcb.udp = udp_new_ip_type(iptype);
+ if (msg->conn->pcb.udp != NULL) {
+#if LWIP_UDPLITE
+ if (NETCONNTYPE_ISUDPLITE(msg->conn->type)) {
+ udp_setflags(msg->conn->pcb.udp, UDP_FLAGS_UDPLITE);
+ }
+#endif /* LWIP_UDPLITE */
+ if (NETCONNTYPE_ISUDPNOCHKSUM(msg->conn->type)) {
+ udp_setflags(msg->conn->pcb.udp, UDP_FLAGS_NOCHKSUM);
+ }
+ udp_recv(msg->conn->pcb.udp, recv_udp, msg->conn);
+ }
+ break;
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+ case NETCONN_TCP:
+ msg->conn->pcb.tcp = tcp_new_ip_type(iptype);
+ if (msg->conn->pcb.tcp != NULL) {
+ setup_tcp(msg->conn);
+ }
+ break;
+#endif /* LWIP_TCP */
+ default:
+ /* Unsupported netconn type, e.g. protocol disabled */
+ msg->err = ERR_VAL;
+ return;
+ }
+ if (msg->conn->pcb.ip == NULL) {
+ msg->err = ERR_MEM;
+ }
+}
+
+/**
+ * Create a new pcb of a specific type inside a netconn.
+ * Called from netconn_new_with_proto_and_callback.
+ *
+ * @param m the api_msg_msg describing the connection type
+ */
+void
+lwip_netconn_do_newconn(void *m)
+{
+ struct api_msg *msg = (struct api_msg*)m;
+
+ msg->err = ERR_OK;
+ if (msg->conn->pcb.tcp == NULL) {
+ pcb_new(msg);
+ }
+ /* Else? This "new" connection already has a PCB allocated. */
+ /* Is this an error condition? Should it be deleted? */
+ /* We currently just are happy and return. */
+
+ TCPIP_APIMSG_ACK(msg);
+}
+
+/**
+ * Create a new netconn (of a specific type) that has a callback function.
+ * The corresponding pcb is NOT created!
+ *
+ * @param t the type of 'connection' to create (@see enum netconn_type)
+ * @param callback a function to call on status changes (RX available, TX'ed)
+ * @return a newly allocated struct netconn or
+ * NULL on memory error
+ */
+struct netconn*
+netconn_alloc(enum netconn_type t, netconn_callback callback)
+{
+ struct netconn *conn;
+ int size;
+
+ conn = (struct netconn *)memp_malloc(MEMP_NETCONN);
+ if (conn == NULL) {
+ return NULL;
+ }
+
+ conn->last_err = ERR_OK;
+ conn->type = t;
+ conn->pcb.tcp = NULL;
+
+ /* If all sizes are the same, every compiler should optimize this switch to nothing */
+ switch(NETCONNTYPE_GROUP(t)) {
+#if LWIP_RAW
+ case NETCONN_RAW:
+ size = DEFAULT_RAW_RECVMBOX_SIZE;
+ break;
+#endif /* LWIP_RAW */
+#if LWIP_UDP
+ case NETCONN_UDP:
+ size = DEFAULT_UDP_RECVMBOX_SIZE;
+ break;
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+ case NETCONN_TCP:
+ size = DEFAULT_TCP_RECVMBOX_SIZE;
+ break;
+#endif /* LWIP_TCP */
+ default:
+ LWIP_ASSERT("netconn_alloc: undefined netconn_type", 0);
+ goto free_and_return;
+ }
+
+ if (sys_mbox_new(&conn->recvmbox, size) != ERR_OK) {
+ goto free_and_return;
+ }
+#if !LWIP_NETCONN_SEM_PER_THREAD
+ if (sys_sem_new(&conn->op_completed, 0) != ERR_OK) {
+ sys_mbox_free(&conn->recvmbox);
+ goto free_and_return;
+ }
+#endif
+
+#if LWIP_TCP
+ sys_mbox_set_invalid(&conn->acceptmbox);
+#endif
+ conn->state = NETCONN_NONE;
+#if LWIP_SOCKET
+ /* initialize socket to -1 since 0 is a valid socket */
+ conn->socket = -1;
+#endif /* LWIP_SOCKET */
+ conn->callback = callback;
+#if LWIP_TCP
+ conn->current_msg = NULL;
+ conn->write_offset = 0;
+#endif /* LWIP_TCP */
+#if LWIP_SO_SNDTIMEO
+ conn->send_timeout = 0;
+#endif /* LWIP_SO_SNDTIMEO */
+#if LWIP_SO_RCVTIMEO
+ conn->recv_timeout = 0;
+#endif /* LWIP_SO_RCVTIMEO */
+#if LWIP_SO_RCVBUF
+ conn->recv_bufsize = RECV_BUFSIZE_DEFAULT;
+ conn->recv_avail = 0;
+#endif /* LWIP_SO_RCVBUF */
+#if LWIP_SO_LINGER
+ conn->linger = -1;
+#endif /* LWIP_SO_LINGER */
+ conn->flags = 0;
+ return conn;
+free_and_return:
+ memp_free(MEMP_NETCONN, conn);
+ return NULL;
+}
+
+/**
+ * Delete a netconn and all its resources.
+ * The pcb is NOT freed (since we might not be in the right thread context do this).
+ *
+ * @param conn the netconn to free
+ */
+void
+netconn_free(struct netconn *conn)
+{
+ LWIP_ASSERT("PCB must be deallocated outside this function", conn->pcb.tcp == NULL);
+ LWIP_ASSERT("recvmbox must be deallocated before calling this function",
+ !sys_mbox_valid(&conn->recvmbox));
+#if LWIP_TCP
+ LWIP_ASSERT("acceptmbox must be deallocated before calling this function",
+ !sys_mbox_valid(&conn->acceptmbox));
+#endif /* LWIP_TCP */
+
+#if !LWIP_NETCONN_SEM_PER_THREAD
+ sys_sem_free(&conn->op_completed);
+ sys_sem_set_invalid(&conn->op_completed);
+#endif
+
+ memp_free(MEMP_NETCONN, conn);
+}
+
+/**
+ * Delete rcvmbox and acceptmbox of a netconn and free the left-over data in
+ * these mboxes
+ *
+ * @param conn the netconn to free
+ * @bytes_drained bytes drained from recvmbox
+ * @accepts_drained pending connections drained from acceptmbox
+ */
+static void
+netconn_drain(struct netconn *conn)
+{
+ void *mem;
+#if LWIP_TCP
+ struct pbuf *p;
+#endif /* LWIP_TCP */
+
+ /* This runs in tcpip_thread, so we don't need to lock against rx packets */
+
+ /* Delete and drain the recvmbox. */
+ if (sys_mbox_valid(&conn->recvmbox)) {
+ while (sys_mbox_tryfetch(&conn->recvmbox, &mem) != SYS_MBOX_EMPTY) {
+#if LWIP_TCP
+ if (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP) {
+ if (mem != NULL) {
+ p = (struct pbuf*)mem;
+ /* pcb might be set to NULL already by err_tcp() */
+ if (conn->pcb.tcp != NULL) {
+ tcp_recved(conn->pcb.tcp, p->tot_len);
+ }
+ pbuf_free(p);
+ }
+ } else
+#endif /* LWIP_TCP */
+ {
+ netbuf_delete((struct netbuf *)mem);
+ }
+ }
+ sys_mbox_free(&conn->recvmbox);
+ sys_mbox_set_invalid(&conn->recvmbox);
+ }
+
+ /* Delete and drain the acceptmbox. */
+#if LWIP_TCP
+ if (sys_mbox_valid(&conn->acceptmbox)) {
+ while (sys_mbox_tryfetch(&conn->acceptmbox, &mem) != SYS_MBOX_EMPTY) {
+ if (mem != &netconn_aborted) {
+ struct netconn *newconn = (struct netconn *)mem;
+ /* Only tcp pcbs have an acceptmbox, so no need to check conn->type */
+ /* pcb might be set to NULL already by err_tcp() */
+ /* drain recvmbox */
+ netconn_drain(newconn);
+ if (newconn->pcb.tcp != NULL) {
+ tcp_abort(newconn->pcb.tcp);
+ newconn->pcb.tcp = NULL;
+ }
+ netconn_free(newconn);
+ }
+ }
+ sys_mbox_free(&conn->acceptmbox);
+ sys_mbox_set_invalid(&conn->acceptmbox);
+ }
+#endif /* LWIP_TCP */
+}
+
+#if LWIP_TCP
+/**
+ * Internal helper function to close a TCP netconn: since this sometimes
+ * doesn't work at the first attempt, this function is called from multiple
+ * places.
+ *
+ * @param conn the TCP netconn to close
+ */
+static err_t
+lwip_netconn_do_close_internal(struct netconn *conn WRITE_DELAYED_PARAM)
+{
+ err_t err;
+ u8_t shut, shut_rx, shut_tx, close;
+ u8_t close_finished = 0;
+ struct tcp_pcb* tpcb;
+#if LWIP_SO_LINGER
+ u8_t linger_wait_required = 0;
+#endif /* LWIP_SO_LINGER */
+
+ LWIP_ASSERT("invalid conn", (conn != NULL));
+ LWIP_ASSERT("this is for tcp netconns only", (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP));
+ LWIP_ASSERT("conn must be in state NETCONN_CLOSE", (conn->state == NETCONN_CLOSE));
+ LWIP_ASSERT("pcb already closed", (conn->pcb.tcp != NULL));
+ LWIP_ASSERT("conn->current_msg != NULL", conn->current_msg != NULL);
+
+ tpcb = conn->pcb.tcp;
+ shut = conn->current_msg->msg.sd.shut;
+ shut_rx = shut & NETCONN_SHUT_RD;
+ shut_tx = shut & NETCONN_SHUT_WR;
+ /* shutting down both ends is the same as closing
+ (also if RD or WR side was shut down before already) */
+ if (shut == NETCONN_SHUT_RDWR) {
+ close = 1;
+ } else if (shut_rx &&
+ ((tpcb->state == FIN_WAIT_1) ||
+ (tpcb->state == FIN_WAIT_2) ||
+ (tpcb->state == CLOSING))) {
+ close = 1;
+ } else if (shut_tx && ((tpcb->flags & TF_RXCLOSED) != 0)) {
+ close = 1;
+ } else {
+ close = 0;
+ }
+
+ /* Set back some callback pointers */
+ if (close) {
+ tcp_arg(tpcb, NULL);
+ }
+ if (tpcb->state == LISTEN) {
+ tcp_accept(tpcb, NULL);
+ } else {
+ /* some callbacks have to be reset if tcp_close is not successful */
+ if (shut_rx) {
+ tcp_recv(tpcb, NULL);
+ tcp_accept(tpcb, NULL);
+ }
+ if (shut_tx) {
+ tcp_sent(tpcb, NULL);
+ }
+ if (close) {
+ tcp_poll(tpcb, NULL, 0);
+ tcp_err(tpcb, NULL);
+ }
+ }
+ /* Try to close the connection */
+ if (close) {
+#if LWIP_SO_LINGER
+ /* check linger possibilites before calling tcp_close */
+ err = ERR_OK;
+ /* linger enabled/required at all? (i.e. is there untransmitted data left?) */
+ if ((conn->linger >= 0) && (conn->pcb.tcp->unsent || conn->pcb.tcp->unacked)) {
+ if ((conn->linger == 0)) {
+ /* data left but linger prevents waiting */
+ tcp_abort(tpcb);
+ tpcb = NULL;
+ } else if (conn->linger > 0) {
+ /* data left and linger says we should wait */
+ if (netconn_is_nonblocking(conn)) {
+ /* data left on a nonblocking netconn -> cannot linger */
+ err = ERR_WOULDBLOCK;
+ } else if ((s32_t)(sys_now() - conn->current_msg->msg.sd.time_started) >=
+ (conn->linger * 1000)) {
+ /* data left but linger timeout has expired (this happens on further
+ calls to this function through poll_tcp */
+ tcp_abort(tpcb);
+ tpcb = NULL;
+ } else {
+ /* data left -> need to wait for ACK after successful close */
+ linger_wait_required = 1;
+ }
+ }
+ }
+ if ((err == ERR_OK) && (tpcb != NULL))
+#endif /* LWIP_SO_LINGER */
+ {
+ err = tcp_close(tpcb);
+ }
+ } else {
+ err = tcp_shutdown(tpcb, shut_rx, shut_tx);
+ }
+ if (err == ERR_OK) {
+ close_finished = 1;
+#if LWIP_SO_LINGER
+ if (linger_wait_required) {
+ /* wait for ACK of all unsent/unacked data by just getting called again */
+ close_finished = 0;
+ err = ERR_INPROGRESS;
+ }
+#endif /* LWIP_SO_LINGER */
+ } else {
+ if (err == ERR_MEM) {
+ /* Closing failed because of memory shortage, try again later. Even for
+ nonblocking netconns, we have to wait since no standard socket application
+ is prepared for close failing because of resource shortage.
+ Check the timeout: this is kind of an lwip addition to the standard sockets:
+ we wait for some time when failing to allocate a segment for the FIN */
+#if LWIP_SO_SNDTIMEO || LWIP_SO_LINGER
+ s32_t close_timeout = LWIP_TCP_CLOSE_TIMEOUT_MS_DEFAULT;
+#if LWIP_SO_SNDTIMEO
+ if (conn->send_timeout > 0) {
+ close_timeout = conn->send_timeout;
+ }
+#endif /* LWIP_SO_SNDTIMEO */
+#if LWIP_SO_LINGER
+ if (conn->linger >= 0) {
+ /* use linger timeout (seconds) */
+ close_timeout = conn->linger * 1000U;
+ }
+#endif
+ if ((s32_t)(sys_now() - conn->current_msg->msg.sd.time_started) >= close_timeout) {
+#else /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */
+ if (conn->current_msg->msg.sd.polls_left == 0) {
+#endif /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */
+ close_finished = 1;
+ if (close) {
+ /* in this case, we want to RST the connection */
+ tcp_abort(tpcb);
+ err = ERR_OK;
+ }
+ }
+ } else {
+ /* Closing failed for a non-memory error: give up */
+ close_finished = 1;
+ }
+ }
+ if (close_finished) {
+ /* Closing done (succeeded, non-memory error, nonblocking error or timeout) */
+ sys_sem_t* op_completed_sem = LWIP_API_MSG_SEM(conn->current_msg);
+ conn->current_msg->err = err;
+ conn->current_msg = NULL;
+ conn->state = NETCONN_NONE;
+ if (err == ERR_OK) {
+ if (close) {
+ /* Set back some callback pointers as conn is going away */
+ conn->pcb.tcp = NULL;
+ /* Trigger select() in socket layer. Make sure everybody notices activity
+ on the connection, error first! */
+ API_EVENT(conn, NETCONN_EVT_ERROR, 0);
+ }
+ if (shut_rx) {
+ API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0);
+ }
+ if (shut_tx) {
+ API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0);
+ }
+ }
+ NETCONN_SET_SAFE_ERR(conn, err);
+#if LWIP_TCPIP_CORE_LOCKING
+ if (delayed)
+#endif
+ {
+ /* wake up the application task */
+ sys_sem_signal(op_completed_sem);
+ }
+ return ERR_OK;
+ }
+ if (!close_finished) {
+ /* Closing failed and we want to wait: restore some of the callbacks */
+ /* Closing of listen pcb will never fail! */
+ LWIP_ASSERT("Closing a listen pcb may not fail!", (tpcb->state != LISTEN));
+ if (shut_tx) {
+ tcp_sent(tpcb, sent_tcp);
+ }
+ /* when waiting for close, set up poll interval to 500ms */
+ tcp_poll(tpcb, poll_tcp, 1);
+ tcp_err(tpcb, err_tcp);
+ tcp_arg(tpcb, conn);
+ /* don't restore recv callback: we don't want to receive any more data */
+ }
+ /* If closing didn't succeed, we get called again either
+ from poll_tcp or from sent_tcp */
+ LWIP_ASSERT("err != ERR_OK", err != ERR_OK);
+ return err;
+}
+#endif /* LWIP_TCP */
+
+/**
+ * Delete the pcb inside a netconn.
+ * Called from netconn_delete.
+ *
+ * @param m the api_msg_msg pointing to the connection
+ */
+void
+lwip_netconn_do_delconn(void *m)
+{
+ struct api_msg *msg = (struct api_msg*)m;
+
+ enum netconn_state state = msg->conn->state;
+ LWIP_ASSERT("netconn state error", /* this only happens for TCP netconns */
+ (state == NETCONN_NONE) || (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP));
+#if LWIP_NETCONN_FULLDUPLEX
+ /* In full duplex mode, blocking write/connect is aborted with ERR_CLSD */
+ if (state != NETCONN_NONE) {
+ if ((state == NETCONN_WRITE) ||
+ ((state == NETCONN_CONNECT) && !IN_NONBLOCKING_CONNECT(msg->conn))) {
+ /* close requested, abort running write/connect */
+ sys_sem_t* op_completed_sem;
+ LWIP_ASSERT("msg->conn->current_msg != NULL", msg->conn->current_msg != NULL);
+ op_completed_sem = LWIP_API_MSG_SEM(msg->conn->current_msg);
+ msg->conn->current_msg->err = ERR_CLSD;
+ msg->conn->current_msg = NULL;
+ msg->conn->write_offset = 0;
+ msg->conn->state = NETCONN_NONE;
+ NETCONN_SET_SAFE_ERR(msg->conn, ERR_CLSD);
+ sys_sem_signal(op_completed_sem);
+ }
+ }
+#else /* LWIP_NETCONN_FULLDUPLEX */
+ if (((state != NETCONN_NONE) &&
+ (state != NETCONN_LISTEN) &&
+ (state != NETCONN_CONNECT)) ||
+ ((state == NETCONN_CONNECT) && !IN_NONBLOCKING_CONNECT(msg->conn))) {
+ /* This means either a blocking write or blocking connect is running
+ (nonblocking write returns and sets state to NONE) */
+ msg->err = ERR_INPROGRESS;
+ } else
+#endif /* LWIP_NETCONN_FULLDUPLEX */
+ {
+ LWIP_ASSERT("blocking connect in progress",
+ (state != NETCONN_CONNECT) || IN_NONBLOCKING_CONNECT(msg->conn));
+ msg->err = ERR_OK;
+ /* Drain and delete mboxes */
+ netconn_drain(msg->conn);
+
+ if (msg->conn->pcb.tcp != NULL) {
+
+ switch (NETCONNTYPE_GROUP(msg->conn->type)) {
+#if LWIP_RAW
+ case NETCONN_RAW:
+ raw_remove(msg->conn->pcb.raw);
+ break;
+#endif /* LWIP_RAW */
+#if LWIP_UDP
+ case NETCONN_UDP:
+ msg->conn->pcb.udp->recv_arg = NULL;
+ udp_remove(msg->conn->pcb.udp);
+ break;
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+ case NETCONN_TCP:
+ LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL &&
+ msg->conn->write_offset == 0);
+ msg->conn->state = NETCONN_CLOSE;
+ msg->msg.sd.shut = NETCONN_SHUT_RDWR;
+ msg->conn->current_msg = msg;
+#if LWIP_TCPIP_CORE_LOCKING
+ if (lwip_netconn_do_close_internal(msg->conn, 0) != ERR_OK) {
+ LWIP_ASSERT("state!", msg->conn->state == NETCONN_CLOSE);
+ UNLOCK_TCPIP_CORE();
+ sys_arch_sem_wait(LWIP_API_MSG_SEM(msg), 0);
+ LOCK_TCPIP_CORE();
+ LWIP_ASSERT("state!", msg->conn->state == NETCONN_NONE);
+ }
+#else /* LWIP_TCPIP_CORE_LOCKING */
+ lwip_netconn_do_close_internal(msg->conn);
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+ /* API_EVENT is called inside lwip_netconn_do_close_internal, before releasing
+ the application thread, so we can return at this point! */
+ return;
+#endif /* LWIP_TCP */
+ default:
+ break;
+ }
+ msg->conn->pcb.tcp = NULL;
+ }
+ /* tcp netconns don't come here! */
+
+ /* @todo: this lets select make the socket readable and writable,
+ which is wrong! errfd instead? */
+ API_EVENT(msg->conn, NETCONN_EVT_RCVPLUS, 0);
+ API_EVENT(msg->conn, NETCONN_EVT_SENDPLUS, 0);
+ }
+ if (sys_sem_valid(LWIP_API_MSG_SEM(msg))) {
+ TCPIP_APIMSG_ACK(msg);
+ }
+}
+
+/**
+ * Bind a pcb contained in a netconn
+ * Called from netconn_bind.
+ *
+ * @param m the api_msg_msg pointing to the connection and containing
+ * the IP address and port to bind to
+ */
+void
+lwip_netconn_do_bind(void *m)
+{
+ struct api_msg *msg = (struct api_msg*)m;
+
+ if (ERR_IS_FATAL(msg->conn->last_err)) {
+ msg->err = msg->conn->last_err;
+ } else {
+ msg->err = ERR_VAL;
+ if (msg->conn->pcb.tcp != NULL) {
+ switch (NETCONNTYPE_GROUP(msg->conn->type)) {
+#if LWIP_RAW
+ case NETCONN_RAW:
+ msg->err = raw_bind(msg->conn->pcb.raw, API_EXPR_REF(msg->msg.bc.ipaddr));
+ break;
+#endif /* LWIP_RAW */
+#if LWIP_UDP
+ case NETCONN_UDP:
+ msg->err = udp_bind(msg->conn->pcb.udp, API_EXPR_REF(msg->msg.bc.ipaddr), msg->msg.bc.port);
+ break;
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+ case NETCONN_TCP:
+ msg->err = tcp_bind(msg->conn->pcb.tcp, API_EXPR_REF(msg->msg.bc.ipaddr), msg->msg.bc.port);
+ break;
+#endif /* LWIP_TCP */
+ default:
+ break;
+ }
+ }
+ }
+ TCPIP_APIMSG_ACK(msg);
+}
+
+#if LWIP_TCP
+/**
+ * TCP callback function if a connection (opened by tcp_connect/lwip_netconn_do_connect) has
+ * been established (or reset by the remote host).
+ *
+ * @see tcp.h (struct tcp_pcb.connected) for parameters and return values
+ */
+static err_t
+lwip_netconn_do_connected(void *arg, struct tcp_pcb *pcb, err_t err)
+{
+ struct netconn *conn;
+ int was_blocking;
+ sys_sem_t* op_completed_sem = NULL;
+
+ LWIP_UNUSED_ARG(pcb);
+
+ conn = (struct netconn *)arg;
+
+ if (conn == NULL) {
+ return ERR_VAL;
+ }
+
+ LWIP_ASSERT("conn->state == NETCONN_CONNECT", conn->state == NETCONN_CONNECT);
+ LWIP_ASSERT("(conn->current_msg != NULL) || conn->in_non_blocking_connect",
+ (conn->current_msg != NULL) || IN_NONBLOCKING_CONNECT(conn));
+
+ if (conn->current_msg != NULL) {
+ conn->current_msg->err = err;
+ op_completed_sem = LWIP_API_MSG_SEM(conn->current_msg);
+ }
+ if ((NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP) && (err == ERR_OK)) {
+ setup_tcp(conn);
+ }
+ was_blocking = !IN_NONBLOCKING_CONNECT(conn);
+ SET_NONBLOCKING_CONNECT(conn, 0);
+ LWIP_ASSERT("blocking connect state error",
+ (was_blocking && op_completed_sem != NULL) ||
+ (!was_blocking && op_completed_sem == NULL));
+ conn->current_msg = NULL;
+ conn->state = NETCONN_NONE;
+ NETCONN_SET_SAFE_ERR(conn, ERR_OK);
+ API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0);
+
+ if (was_blocking) {
+ sys_sem_signal(op_completed_sem);
+ }
+ return ERR_OK;
+}
+#endif /* LWIP_TCP */
+
+/**
+ * Connect a pcb contained inside a netconn
+ * Called from netconn_connect.
+ *
+ * @param m the api_msg_msg pointing to the connection and containing
+ * the IP address and port to connect to
+ */
+void
+lwip_netconn_do_connect(void *m)
+{
+ struct api_msg *msg = (struct api_msg*)m;
+
+ if (msg->conn->pcb.tcp == NULL) {
+ /* This may happen when calling netconn_connect() a second time */
+ msg->err = ERR_CLSD;
+ } else {
+ switch (NETCONNTYPE_GROUP(msg->conn->type)) {
+#if LWIP_RAW
+ case NETCONN_RAW:
+ msg->err = raw_connect(msg->conn->pcb.raw, API_EXPR_REF(msg->msg.bc.ipaddr));
+ break;
+#endif /* LWIP_RAW */
+#if LWIP_UDP
+ case NETCONN_UDP:
+ msg->err = udp_connect(msg->conn->pcb.udp, API_EXPR_REF(msg->msg.bc.ipaddr), msg->msg.bc.port);
+ break;
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+ case NETCONN_TCP:
+ /* Prevent connect while doing any other action. */
+ if (msg->conn->state == NETCONN_CONNECT) {
+ msg->err = ERR_ALREADY;
+ } else if (msg->conn->state != NETCONN_NONE) {
+ msg->err = ERR_ISCONN;
+ } else {
+ setup_tcp(msg->conn);
+ msg->err = tcp_connect(msg->conn->pcb.tcp, API_EXPR_REF(msg->msg.bc.ipaddr),
+ msg->msg.bc.port, lwip_netconn_do_connected);
+ if (msg->err == ERR_OK) {
+ u8_t non_blocking = netconn_is_nonblocking(msg->conn);
+ msg->conn->state = NETCONN_CONNECT;
+ SET_NONBLOCKING_CONNECT(msg->conn, non_blocking);
+ if (non_blocking) {
+ msg->err = ERR_INPROGRESS;
+ } else {
+ msg->conn->current_msg = msg;
+ /* sys_sem_signal() is called from lwip_netconn_do_connected (or err_tcp()),
+ when the connection is established! */
+#if LWIP_TCPIP_CORE_LOCKING
+ LWIP_ASSERT("state!", msg->conn->state == NETCONN_CONNECT);
+ UNLOCK_TCPIP_CORE();
+ sys_arch_sem_wait(LWIP_API_MSG_SEM(msg), 0);
+ LOCK_TCPIP_CORE();
+ LWIP_ASSERT("state!", msg->conn->state != NETCONN_CONNECT);
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+ return;
+ }
+ }
+ }
+ break;
+#endif /* LWIP_TCP */
+ default:
+ LWIP_ERROR("Invalid netconn type", 0, do{ msg->err = ERR_VAL; }while(0));
+ break;
+ }
+ }
+ /* For all other protocols, netconn_connect() calls TCPIP_APIMSG(),
+ so use TCPIP_APIMSG_ACK() here. */
+ TCPIP_APIMSG_ACK(msg);
+}
+
+/**
+ * Disconnect a pcb contained inside a netconn
+ * Only used for UDP netconns.
+ * Called from netconn_disconnect.
+ *
+ * @param m the api_msg_msg pointing to the connection to disconnect
+ */
+void
+lwip_netconn_do_disconnect(void *m)
+{
+ struct api_msg *msg = (struct api_msg*)m;
+
+#if LWIP_UDP
+ if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_UDP) {
+ udp_disconnect(msg->conn->pcb.udp);
+ msg->err = ERR_OK;
+ } else
+#endif /* LWIP_UDP */
+ {
+ msg->err = ERR_VAL;
+ }
+ TCPIP_APIMSG_ACK(msg);
+}
+
+#if LWIP_TCP
+/**
+ * Set a TCP pcb contained in a netconn into listen mode
+ * Called from netconn_listen.
+ *
+ * @param m the api_msg_msg pointing to the connection
+ */
+void
+lwip_netconn_do_listen(void *m)
+{
+ struct api_msg *msg = (struct api_msg*)m;
+
+ if (ERR_IS_FATAL(msg->conn->last_err)) {
+ msg->err = msg->conn->last_err;
+ } else {
+ msg->err = ERR_CONN;
+ if (msg->conn->pcb.tcp != NULL) {
+ if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) {
+ if (msg->conn->state == NETCONN_NONE) {
+ struct tcp_pcb* lpcb;
+ if (msg->conn->pcb.tcp->state != CLOSED) {
+ /* connection is not closed, cannot listen */
+ msg->err = ERR_VAL;
+ } else {
+ err_t err;
+ u8_t backlog;
+#if TCP_LISTEN_BACKLOG
+ backlog = msg->msg.lb.backlog;
+#else /* TCP_LISTEN_BACKLOG */
+ backlog = TCP_DEFAULT_LISTEN_BACKLOG;
+#endif /* TCP_LISTEN_BACKLOG */
+#if LWIP_IPV4 && LWIP_IPV6
+ /* "Socket API like" dual-stack support: If IP to listen to is IP6_ADDR_ANY,
+ * and NETCONN_FLAG_IPV6_V6ONLY is NOT set, use IP_ANY_TYPE to listen
+ */
+ if (ip_addr_cmp(&msg->conn->pcb.ip->local_ip, IP6_ADDR_ANY) &&
+ (netconn_get_ipv6only(msg->conn) == 0)) {
+ /* change PCB type to IPADDR_TYPE_ANY */
+ IP_SET_TYPE_VAL(msg->conn->pcb.tcp->local_ip, IPADDR_TYPE_ANY);
+ IP_SET_TYPE_VAL(msg->conn->pcb.tcp->remote_ip, IPADDR_TYPE_ANY);
+ }
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+
+ lpcb = tcp_listen_with_backlog_and_err(msg->conn->pcb.tcp, backlog, &err);
+
+ if (lpcb == NULL) {
+ /* in this case, the old pcb is still allocated */
+ msg->err = err;
+ } else {
+ /* delete the recvmbox and allocate the acceptmbox */
+ if (sys_mbox_valid(&msg->conn->recvmbox)) {
+ /** @todo: should we drain the recvmbox here? */
+ sys_mbox_free(&msg->conn->recvmbox);
+ sys_mbox_set_invalid(&msg->conn->recvmbox);
+ }
+ msg->err = ERR_OK;
+ if (!sys_mbox_valid(&msg->conn->acceptmbox)) {
+ msg->err = sys_mbox_new(&msg->conn->acceptmbox, DEFAULT_ACCEPTMBOX_SIZE);
+ }
+ if (msg->err == ERR_OK) {
+ msg->conn->state = NETCONN_LISTEN;
+ msg->conn->pcb.tcp = lpcb;
+ tcp_arg(msg->conn->pcb.tcp, msg->conn);
+ tcp_accept(msg->conn->pcb.tcp, accept_function);
+ } else {
+ /* since the old pcb is already deallocated, free lpcb now */
+ tcp_close(lpcb);
+ msg->conn->pcb.tcp = NULL;
+ }
+ }
+ }
+ } else if (msg->conn->state == NETCONN_LISTEN) {
+ /* already listening, allow updating of the backlog */
+ msg->err = ERR_OK;
+ tcp_backlog_set(msg->conn->pcb.tcp, msg->msg.lb.backlog);
+ }
+ } else {
+ msg->err = ERR_ARG;
+ }
+ }
+ }
+ TCPIP_APIMSG_ACK(msg);
+}
+#endif /* LWIP_TCP */
+
+/**
+ * Send some data on a RAW or UDP pcb contained in a netconn
+ * Called from netconn_send
+ *
+ * @param m the api_msg_msg pointing to the connection
+ */
+void
+lwip_netconn_do_send(void *m)
+{
+ struct api_msg *msg = (struct api_msg*)m;
+
+ if (ERR_IS_FATAL(msg->conn->last_err)) {
+ msg->err = msg->conn->last_err;
+ } else {
+ msg->err = ERR_CONN;
+ if (msg->conn->pcb.tcp != NULL) {
+ switch (NETCONNTYPE_GROUP(msg->conn->type)) {
+#if LWIP_RAW
+ case NETCONN_RAW:
+ if (ip_addr_isany(&msg->msg.b->addr) || IP_IS_ANY_TYPE_VAL(msg->msg.b->addr)) {
+ msg->err = raw_send(msg->conn->pcb.raw, msg->msg.b->p);
+ } else {
+ msg->err = raw_sendto(msg->conn->pcb.raw, msg->msg.b->p, &msg->msg.b->addr);
+ }
+ break;
+#endif
+#if LWIP_UDP
+ case NETCONN_UDP:
+#if LWIP_CHECKSUM_ON_COPY
+ if (ip_addr_isany(&msg->msg.b->addr) || IP_IS_ANY_TYPE_VAL(msg->msg.b->addr)) {
+ msg->err = udp_send_chksum(msg->conn->pcb.udp, msg->msg.b->p,
+ msg->msg.b->flags & NETBUF_FLAG_CHKSUM, msg->msg.b->toport_chksum);
+ } else {
+ msg->err = udp_sendto_chksum(msg->conn->pcb.udp, msg->msg.b->p,
+ &msg->msg.b->addr, msg->msg.b->port,
+ msg->msg.b->flags & NETBUF_FLAG_CHKSUM, msg->msg.b->toport_chksum);
+ }
+#else /* LWIP_CHECKSUM_ON_COPY */
+ if (ip_addr_isany_val(msg->msg.b->addr) || IP_IS_ANY_TYPE_VAL(msg->msg.b->addr)) {
+ msg->err = udp_send(msg->conn->pcb.udp, msg->msg.b->p);
+ } else {
+ msg->err = udp_sendto(msg->conn->pcb.udp, msg->msg.b->p, &msg->msg.b->addr, msg->msg.b->port);
+ }
+#endif /* LWIP_CHECKSUM_ON_COPY */
+ break;
+#endif /* LWIP_UDP */
+ default:
+ break;
+ }
+ }
+ }
+ TCPIP_APIMSG_ACK(msg);
+}
+
+#if LWIP_TCP
+/**
+ * Indicate data has been received from a TCP pcb contained in a netconn
+ * Called from netconn_recv
+ *
+ * @param m the api_msg_msg pointing to the connection
+ */
+void
+lwip_netconn_do_recv(void *m)
+{
+ struct api_msg *msg = (struct api_msg*)m;
+
+ msg->err = ERR_OK;
+ if (msg->conn->pcb.tcp != NULL) {
+ if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) {
+ u32_t remaining = msg->msg.r.len;
+ do {
+ u16_t recved = (remaining > 0xffff) ? 0xffff : (u16_t)remaining;
+ tcp_recved(msg->conn->pcb.tcp, recved);
+ remaining -= recved;
+ } while (remaining != 0);
+ }
+ }
+ TCPIP_APIMSG_ACK(msg);
+}
+
+#if TCP_LISTEN_BACKLOG
+/** Indicate that a TCP pcb has been accepted
+ * Called from netconn_accept
+ *
+ * @param m the api_msg_msg pointing to the connection
+ */
+void
+lwip_netconn_do_accepted(void *m)
+{
+ struct api_msg *msg = (struct api_msg*)m;
+
+ msg->err = ERR_OK;
+ if (msg->conn->pcb.tcp != NULL) {
+ if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) {
+ tcp_backlog_accepted(msg->conn->pcb.tcp);
+ }
+ }
+ TCPIP_APIMSG_ACK(msg);
+}
+#endif /* TCP_LISTEN_BACKLOG */
+
+/**
+ * See if more data needs to be written from a previous call to netconn_write.
+ * Called initially from lwip_netconn_do_write. If the first call can't send all data
+ * (because of low memory or empty send-buffer), this function is called again
+ * from sent_tcp() or poll_tcp() to send more data. If all data is sent, the
+ * blocking application thread (waiting in netconn_write) is released.
+ *
+ * @param conn netconn (that is currently in state NETCONN_WRITE) to process
+ * @return ERR_OK
+ * ERR_MEM if LWIP_TCPIP_CORE_LOCKING=1 and sending hasn't yet finished
+ */
+static err_t
+lwip_netconn_do_writemore(struct netconn *conn WRITE_DELAYED_PARAM)
+{
+ err_t err;
+ const void *dataptr;
+ u16_t len, available;
+ u8_t write_finished = 0;
+ size_t diff;
+ u8_t dontblock;
+ u8_t apiflags;
+
+ LWIP_ASSERT("conn != NULL", conn != NULL);
+ LWIP_ASSERT("conn->state == NETCONN_WRITE", (conn->state == NETCONN_WRITE));
+ LWIP_ASSERT("conn->current_msg != NULL", conn->current_msg != NULL);
+ LWIP_ASSERT("conn->pcb.tcp != NULL", conn->pcb.tcp != NULL);
+ LWIP_ASSERT("conn->write_offset < conn->current_msg->msg.w.len",
+ conn->write_offset < conn->current_msg->msg.w.len);
+
+ apiflags = conn->current_msg->msg.w.apiflags;
+ dontblock = netconn_is_nonblocking(conn) || (apiflags & NETCONN_DONTBLOCK);
+
+#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;
+ conn->write_offset = 0;
+ }
+ } else
+#endif /* LWIP_SO_SNDTIMEO */
+ {
+ dataptr = (const 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;
+ 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 {
+ apiflags |= TCP_WRITE_FLAG_MORE;
+ }
+ }
+ LWIP_ASSERT("lwip_netconn_do_writemore: invalid length!", ((conn->write_offset + len) <= conn->current_msg->msg.w.len));
+ err = tcp_write(conn->pcb.tcp, dataptr, len, apiflags);
+ /* if OK or memory error, check available space */
+ if ((err == ERR_OK) || (err == ERR_MEM)) {
+err_mem:
+ if (dontblock && (len < conn->current_msg->msg.w.len)) {
+ /* non-blocking write did not write everything: mark the pcb non-writable
+ and let poll_tcp check writable space to mark the pcb writable again */
+ API_EVENT(conn, NETCONN_EVT_SENDMINUS, len);
+ conn->flags |= NETCONN_FLAG_CHECK_WRITESPACE;
+ } else if ((tcp_sndbuf(conn->pcb.tcp) <= TCP_SNDLOWAT) ||
+ (tcp_sndqueuelen(conn->pcb.tcp) >= TCP_SNDQUEUELOWAT)) {
+ /* The queued byte- or pbuf-count exceeds the configured low-water limit,
+ let select mark this pcb as non-writable. */
+ API_EVENT(conn, NETCONN_EVT_SENDMINUS, len);
+ }
+ }
+
+ if (err == ERR_OK) {
+ err_t out_err;
+ conn->write_offset += len;
+ if ((conn->write_offset == conn->current_msg->msg.w.len) || dontblock) {
+ /* return sent length */
+ conn->current_msg->msg.w.len = conn->write_offset;
+ /* everything was written */
+ write_finished = 1;
+ }
+ out_err = tcp_output(conn->pcb.tcp);
+ if (ERR_IS_FATAL(out_err) || (out_err == ERR_RTE)) {
+ /* If tcp_output fails with fatal error or no route is found,
+ don't try writing any more but return the error
+ to the application thread. */
+ err = out_err;
+ write_finished = 1;
+ conn->current_msg->msg.w.len = 0;
+ }
+ } else if (err == ERR_MEM) {
+ /* If ERR_MEM, we wait for sent_tcp or poll_tcp to be called.
+ For blocking sockets, we do NOT return to the application
+ thread, since ERR_MEM is only a temporary error! Non-blocking
+ will remain non-writable until sent_tcp/poll_tcp is called */
+
+ /* tcp_write returned ERR_MEM, try tcp_output anyway */
+ err_t out_err = tcp_output(conn->pcb.tcp);
+ if (ERR_IS_FATAL(out_err) || (out_err == ERR_RTE)) {
+ /* If tcp_output fails with fatal error or no route is found,
+ don't try writing any more but return the error
+ to the application thread. */
+ err = out_err;
+ write_finished = 1;
+ conn->current_msg->msg.w.len = 0;
+ } else if (dontblock) {
+ /* non-blocking write is done on ERR_MEM */
+ err = ERR_WOULDBLOCK;
+ write_finished = 1;
+ conn->current_msg->msg.w.len = 0;
+ }
+ } 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 */
+ sys_sem_t* op_completed_sem = LWIP_API_MSG_SEM(conn->current_msg);
+ conn->current_msg->err = err;
+ conn->current_msg = NULL;
+ conn->write_offset = 0;
+ conn->state = NETCONN_NONE;
+ NETCONN_SET_SAFE_ERR(conn, err);
+#if LWIP_TCPIP_CORE_LOCKING
+ if (delayed)
+#endif
+ {
+ sys_sem_signal(op_completed_sem);
+ }
+ }
+#if LWIP_TCPIP_CORE_LOCKING
+ else {
+ return ERR_MEM;
+ }
+#endif
+ return ERR_OK;
+}
+#endif /* LWIP_TCP */
+
+/**
+ * Send some data on a TCP pcb contained in a netconn
+ * Called from netconn_write
+ *
+ * @param m the api_msg_msg pointing to the connection
+ */
+void
+lwip_netconn_do_write(void *m)
+{
+ struct api_msg *msg = (struct api_msg*)m;
+
+ if (ERR_IS_FATAL(msg->conn->last_err)) {
+ msg->err = msg->conn->last_err;
+ } else {
+ if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) {
+#if LWIP_TCP
+ if (msg->conn->state != NETCONN_NONE) {
+ /* netconn is connecting, closing or in blocking write */
+ msg->err = ERR_INPROGRESS;
+ } else if (msg->conn->pcb.tcp != NULL) {
+ msg->conn->state = NETCONN_WRITE;
+ /* set all the variables used by lwip_netconn_do_writemore */
+ LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL &&
+ msg->conn->write_offset == 0);
+ LWIP_ASSERT("msg->msg.w.len != 0", msg->msg.w.len != 0);
+ msg->conn->current_msg = msg;
+ msg->conn->write_offset = 0;
+#if LWIP_TCPIP_CORE_LOCKING
+ if (lwip_netconn_do_writemore(msg->conn, 0) != ERR_OK) {
+ LWIP_ASSERT("state!", msg->conn->state == NETCONN_WRITE);
+ UNLOCK_TCPIP_CORE();
+ sys_arch_sem_wait(LWIP_API_MSG_SEM(msg), 0);
+ LOCK_TCPIP_CORE();
+ LWIP_ASSERT("state!", msg->conn->state != NETCONN_WRITE);
+ }
+#else /* LWIP_TCPIP_CORE_LOCKING */
+ lwip_netconn_do_writemore(msg->conn);
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+ /* for both cases: if lwip_netconn_do_writemore was called, don't ACK the APIMSG
+ since lwip_netconn_do_writemore ACKs it! */
+ return;
+ } else {
+ msg->err = ERR_CONN;
+ }
+#else /* LWIP_TCP */
+ msg->err = ERR_VAL;
+#endif /* LWIP_TCP */
+#if (LWIP_UDP || LWIP_RAW)
+ } else {
+ msg->err = ERR_VAL;
+#endif /* (LWIP_UDP || LWIP_RAW) */
+ }
+ }
+ TCPIP_APIMSG_ACK(msg);
+}
+
+/**
+ * Return a connection's local or remote address
+ * Called from netconn_getaddr
+ *
+ * @param m the api_msg_msg pointing to the connection
+ */
+void
+lwip_netconn_do_getaddr(void *m)
+{
+ struct api_msg *msg = (struct api_msg*)m;
+
+ if (msg->conn->pcb.ip != NULL) {
+ if (msg->msg.ad.local) {
+ ip_addr_copy(API_EXPR_DEREF(msg->msg.ad.ipaddr),
+ msg->conn->pcb.ip->local_ip);
+ } else {
+ ip_addr_copy(API_EXPR_DEREF(msg->msg.ad.ipaddr),
+ msg->conn->pcb.ip->remote_ip);
+ }
+
+ msg->err = ERR_OK;
+ switch (NETCONNTYPE_GROUP(msg->conn->type)) {
+#if LWIP_RAW
+ case NETCONN_RAW:
+ if (msg->msg.ad.local) {
+ API_EXPR_DEREF(msg->msg.ad.port) = msg->conn->pcb.raw->protocol;
+ } else {
+ /* return an error as connecting is only a helper for upper layers */
+ msg->err = ERR_CONN;
+ }
+ break;
+#endif /* LWIP_RAW */
+#if LWIP_UDP
+ case NETCONN_UDP:
+ if (msg->msg.ad.local) {
+ API_EXPR_DEREF(msg->msg.ad.port) = msg->conn->pcb.udp->local_port;
+ } else {
+ if ((msg->conn->pcb.udp->flags & UDP_FLAGS_CONNECTED) == 0) {
+ msg->err = ERR_CONN;
+ } else {
+ API_EXPR_DEREF(msg->msg.ad.port) = msg->conn->pcb.udp->remote_port;
+ }
+ }
+ break;
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+ case NETCONN_TCP:
+ if ((msg->msg.ad.local == 0) &&
+ ((msg->conn->pcb.tcp->state == CLOSED) || (msg->conn->pcb.tcp->state == LISTEN))) {
+ /* pcb is not connected and remote name is requested */
+ msg->err = ERR_CONN;
+ } else {
+ API_EXPR_DEREF(msg->msg.ad.port) = (msg->msg.ad.local ? msg->conn->pcb.tcp->local_port : msg->conn->pcb.tcp->remote_port);
+ }
+ break;
+#endif /* LWIP_TCP */
+ default:
+ LWIP_ASSERT("invalid netconn_type", 0);
+ break;
+ }
+ } else {
+ msg->err = ERR_CONN;
+ }
+ TCPIP_APIMSG_ACK(msg);
+}
+
+/**
+ * Close or half-shutdown a TCP pcb contained in a netconn
+ * Called from netconn_close
+ * In contrast to closing sockets, the netconn is not deallocated.
+ *
+ * @param m the api_msg_msg pointing to the connection
+ */
+void
+lwip_netconn_do_close(void *m)
+{
+ struct api_msg *msg = (struct api_msg*)m;
+
+#if LWIP_TCP
+ enum netconn_state state = msg->conn->state;
+ /* First check if this is a TCP netconn and if it is in a correct state
+ (LISTEN doesn't support half shutdown) */
+ if ((msg->conn->pcb.tcp != NULL) &&
+ (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) &&
+ ((msg->msg.sd.shut == NETCONN_SHUT_RDWR) || (state != NETCONN_LISTEN))) {
+ /* Check if we are in a connected state */
+ if (state == NETCONN_CONNECT) {
+ /* TCP connect in progress: cannot shutdown */
+ msg->err = ERR_CONN;
+ } else if (state == NETCONN_WRITE) {
+#if LWIP_NETCONN_FULLDUPLEX
+ if (msg->msg.sd.shut & NETCONN_SHUT_WR) {
+ /* close requested, abort running write */
+ sys_sem_t* write_completed_sem;
+ LWIP_ASSERT("msg->conn->current_msg != NULL", msg->conn->current_msg != NULL);
+ write_completed_sem = LWIP_API_MSG_SEM(msg->conn->current_msg);
+ msg->conn->current_msg->err = ERR_CLSD;
+ msg->conn->current_msg = NULL;
+ msg->conn->write_offset = 0;
+ msg->conn->state = NETCONN_NONE;
+ state = NETCONN_NONE;
+ NETCONN_SET_SAFE_ERR(msg->conn, ERR_CLSD);
+ sys_sem_signal(write_completed_sem);
+ } else {
+ LWIP_ASSERT("msg->msg.sd.shut == NETCONN_SHUT_RD", msg->msg.sd.shut == NETCONN_SHUT_RD);
+ /* In this case, let the write continue and do not interfere with
+ conn->current_msg or conn->state! */
+ msg->err = tcp_shutdown(msg->conn->pcb.tcp, 1, 0);
+ }
+ }
+ if (state == NETCONN_NONE) {
+#else /* LWIP_NETCONN_FULLDUPLEX */
+ msg->err = ERR_INPROGRESS;
+ } else {
+#endif /* LWIP_NETCONN_FULLDUPLEX */
+ if (msg->msg.sd.shut & NETCONN_SHUT_RD) {
+ /* Drain and delete mboxes */
+ netconn_drain(msg->conn);
+ }
+ LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL &&
+ msg->conn->write_offset == 0);
+ msg->conn->state = NETCONN_CLOSE;
+ msg->conn->current_msg = msg;
+#if LWIP_TCPIP_CORE_LOCKING
+ if (lwip_netconn_do_close_internal(msg->conn, 0) != ERR_OK) {
+ LWIP_ASSERT("state!", msg->conn->state == NETCONN_CLOSE);
+ UNLOCK_TCPIP_CORE();
+ sys_arch_sem_wait(LWIP_API_MSG_SEM(msg), 0);
+ LOCK_TCPIP_CORE();
+ LWIP_ASSERT("state!", msg->conn->state == NETCONN_NONE);
+ }
+#else /* LWIP_TCPIP_CORE_LOCKING */
+ lwip_netconn_do_close_internal(msg->conn);
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+ /* for tcp netconns, lwip_netconn_do_close_internal ACKs the message */
+ return;
+ }
+ } else
+#endif /* LWIP_TCP */
+ {
+ msg->err = ERR_CONN;
+ }
+ TCPIP_APIMSG_ACK(msg);
+}
+
+#if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD)
+/**
+ * Join multicast groups for UDP netconns.
+ * Called from netconn_join_leave_group
+ *
+ * @param m the api_msg_msg pointing to the connection
+ */
+void
+lwip_netconn_do_join_leave_group(void *m)
+{
+ struct api_msg *msg = (struct api_msg*)m;
+
+ if (ERR_IS_FATAL(msg->conn->last_err)) {
+ msg->err = msg->conn->last_err;
+ } else {
+ if (msg->conn->pcb.tcp != NULL) {
+ if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_UDP) {
+#if LWIP_UDP
+#if LWIP_IPV6 && LWIP_IPV6_MLD
+ if (NETCONNTYPE_ISIPV6(msg->conn->type)) {
+ if (msg->msg.jl.join_or_leave == NETCONN_JOIN) {
+ msg->err = mld6_joingroup(ip_2_ip6(API_EXPR_REF(msg->msg.jl.netif_addr)),
+ ip_2_ip6(API_EXPR_REF(msg->msg.jl.multiaddr)));
+ } else {
+ msg->err = mld6_leavegroup(ip_2_ip6(API_EXPR_REF(msg->msg.jl.netif_addr)),
+ ip_2_ip6(API_EXPR_REF(msg->msg.jl.multiaddr)));
+ }
+ }
+ else
+#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */
+ {
+#if LWIP_IGMP
+ if (msg->msg.jl.join_or_leave == NETCONN_JOIN) {
+ msg->err = igmp_joingroup(ip_2_ip4(API_EXPR_REF(msg->msg.jl.netif_addr)),
+ ip_2_ip4(API_EXPR_REF(msg->msg.jl.multiaddr)));
+ } else {
+ msg->err = igmp_leavegroup(ip_2_ip4(API_EXPR_REF(msg->msg.jl.netif_addr)),
+ ip_2_ip4(API_EXPR_REF(msg->msg.jl.multiaddr)));
+ }
+#endif /* LWIP_IGMP */
+ }
+#endif /* LWIP_UDP */
+#if (LWIP_TCP || LWIP_RAW)
+ } else {
+ msg->err = ERR_VAL;
+#endif /* (LWIP_TCP || LWIP_RAW) */
+ }
+ } else {
+ msg->err = ERR_CONN;
+ }
+ }
+ TCPIP_APIMSG_ACK(msg);
+}
+#endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */
+
+#if LWIP_DNS
+/**
+ * Callback function that is called when DNS name is resolved
+ * (or on timeout). A waiting application thread is waked up by
+ * signaling the semaphore.
+ */
+static void
+lwip_netconn_do_dns_found(const char *name, const ip_addr_t *ipaddr, void *arg)
+{
+ struct dns_api_msg *msg = (struct dns_api_msg*)arg;
+
+ /* we trust the internal implementation to be correct :-) */
+ LWIP_UNUSED_ARG(name);
+
+ if (ipaddr == NULL) {
+ /* timeout or memory error */
+ API_EXPR_DEREF(msg->err) = ERR_VAL;
+ } else {
+ /* address was resolved */
+ API_EXPR_DEREF(msg->err) = ERR_OK;
+ API_EXPR_DEREF(msg->addr) = *ipaddr;
+ }
+ /* wake up the application task waiting in netconn_gethostbyname */
+ sys_sem_signal(API_EXPR_REF_SEM(msg->sem));
+}
+
+/**
+ * Execute a DNS query
+ * Called from netconn_gethostbyname
+ *
+ * @param arg the dns_api_msg pointing to the query
+ */
+void
+lwip_netconn_do_gethostbyname(void *arg)
+{
+ struct dns_api_msg *msg = (struct dns_api_msg*)arg;
+ u8_t addrtype =
+#if LWIP_IPV4 && LWIP_IPV6
+ msg->dns_addrtype;
+#else
+ LWIP_DNS_ADDRTYPE_DEFAULT;
+#endif
+
+ API_EXPR_DEREF(msg->err) = dns_gethostbyname_addrtype(msg->name,
+ API_EXPR_REF(msg->addr), lwip_netconn_do_dns_found, msg, addrtype);
+ if (API_EXPR_DEREF(msg->err) != ERR_INPROGRESS) {
+ /* on error or immediate success, wake up the application
+ * task waiting in netconn_gethostbyname */
+ sys_sem_signal(API_EXPR_REF_SEM(msg->sem));
+ }
+}
+#endif /* LWIP_DNS */
+
+#endif /* LWIP_NETCONN */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/api/err.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/api/err.c
new file mode 100644
index 0000000..35e9c02
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/api/err.c
@@ -0,0 +1,115 @@
+/**
+ * @file
+ * Error Management module
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/err.h"
+#include "lwip/def.h"
+#include "lwip/sys.h"
+
+#include "lwip/errno.h"
+
+#if !NO_SYS
+/** Table to quickly map an lwIP error (err_t) to a socket error
+ * by using -err as an index */
+static const int err_to_errno_table[] = {
+ 0, /* ERR_OK 0 No error, everything OK. */
+ ENOMEM, /* ERR_MEM -1 Out of memory error. */
+ ENOBUFS, /* ERR_BUF -2 Buffer error. */
+ EWOULDBLOCK, /* ERR_TIMEOUT -3 Timeout */
+ EHOSTUNREACH, /* ERR_RTE -4 Routing problem. */
+ EINPROGRESS, /* ERR_INPROGRESS -5 Operation in progress */
+ EINVAL, /* ERR_VAL -6 Illegal value. */
+ EWOULDBLOCK, /* ERR_WOULDBLOCK -7 Operation would block. */
+ EADDRINUSE, /* ERR_USE -8 Address in use. */
+ EALREADY, /* ERR_ALREADY -9 Already connecting. */
+ EISCONN, /* ERR_ISCONN -10 Conn already established.*/
+ ENOTCONN, /* ERR_CONN -11 Not connected. */
+ -1, /* ERR_IF -12 Low-level netif error */
+ ECONNABORTED, /* ERR_ABRT -13 Connection aborted. */
+ ECONNRESET, /* ERR_RST -14 Connection reset. */
+ ENOTCONN, /* ERR_CLSD -15 Connection closed. */
+ EIO /* ERR_ARG -16 Illegal argument. */
+};
+
+int
+err_to_errno(err_t err)
+{
+ if ((err > 0) || (-err >= (err_t)LWIP_ARRAYSIZE(err_to_errno_table))) {
+ return EIO;
+ }
+ return err_to_errno_table[-err];
+}
+#endif /* !NO_SYS */
+
+#ifdef LWIP_DEBUG
+
+static const char *err_strerr[] = {
+ "Ok.", /* ERR_OK 0 */
+ "Out of memory error.", /* ERR_MEM -1 */
+ "Buffer error.", /* ERR_BUF -2 */
+ "Timeout.", /* ERR_TIMEOUT -3 */
+ "Routing problem.", /* ERR_RTE -4 */
+ "Operation in progress.", /* ERR_INPROGRESS -5 */
+ "Illegal value.", /* ERR_VAL -6 */
+ "Operation would block.", /* ERR_WOULDBLOCK -7 */
+ "Address in use.", /* ERR_USE -8 */
+ "Already connecting.", /* ERR_ALREADY -9 */
+ "Already connected.", /* ERR_ISCONN -10 */
+ "Not connected.", /* ERR_CONN -11 */
+ "Low-level netif error.", /* ERR_IF -12 */
+ "Connection aborted.", /* ERR_ABRT -13 */
+ "Connection reset.", /* ERR_RST -14 */
+ "Connection closed.", /* ERR_CLSD -15 */
+ "Illegal argument." /* ERR_ARG -16 */
+};
+
+/**
+ * Convert an lwip internal error to a string representation.
+ *
+ * @param err an lwip internal err_t
+ * @return a string representation for err
+ */
+const char *
+lwip_strerr(err_t err)
+{
+ if ((err > 0) || (-err >= (err_t)LWIP_ARRAYSIZE(err_strerr))) {
+ return "Unknown error.";
+ }
+ return err_strerr[-err];
+}
+
+#endif /* LWIP_DEBUG */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/api/netbuf.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/api/netbuf.c
new file mode 100644
index 0000000..eb25011
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/api/netbuf.c
@@ -0,0 +1,246 @@
+/**
+ * @file
+ * Network buffer management
+ *
+ * @defgroup netbuf Network buffers
+ * @ingroup netconn
+ * Network buffer descriptor for @ref netconn. Based on @ref pbuf internally
+ * to avoid copying data around.\n
+ * Buffers must not be shared accross multiple threads, all functions except
+ * netbuf_new() and netbuf_delete() are not thread-safe.
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/netbuf.h"
+#include "lwip/memp.h"
+
+#include <string.h>
+
+/**
+ * @ingroup netbuf
+ * Create (allocate) and initialize a new netbuf.
+ * The netbuf doesn't yet contain a packet buffer!
+ *
+ * @return a pointer to a new netbuf
+ * NULL on lack of memory
+ */
+struct
+netbuf *netbuf_new(void)
+{
+ struct netbuf *buf;
+
+ buf = (struct netbuf *)memp_malloc(MEMP_NETBUF);
+ if (buf != NULL) {
+ memset(buf, 0, sizeof(struct netbuf));
+ }
+ return buf;
+}
+
+/**
+ * @ingroup netbuf
+ * Deallocate a netbuf allocated by netbuf_new().
+ *
+ * @param buf pointer to a netbuf allocated by netbuf_new()
+ */
+void
+netbuf_delete(struct netbuf *buf)
+{
+ if (buf != NULL) {
+ if (buf->p != NULL) {
+ pbuf_free(buf->p);
+ buf->p = buf->ptr = NULL;
+ }
+ memp_free(MEMP_NETBUF, buf);
+ }
+}
+
+/**
+ * @ingroup netbuf
+ * Allocate memory for a packet buffer for a given netbuf.
+ *
+ * @param buf the netbuf for which to allocate a packet buffer
+ * @param size the size of the packet buffer to allocate
+ * @return pointer to the allocated memory
+ * NULL if no memory could be allocated
+ */
+void *
+netbuf_alloc(struct netbuf *buf, u16_t size)
+{
+ LWIP_ERROR("netbuf_alloc: invalid buf", (buf != NULL), return NULL;);
+
+ /* Deallocate any previously allocated memory. */
+ if (buf->p != NULL) {
+ pbuf_free(buf->p);
+ }
+ buf->p = pbuf_alloc(PBUF_TRANSPORT, size, PBUF_RAM);
+ if (buf->p == NULL) {
+ return NULL;
+ }
+ LWIP_ASSERT("check that first pbuf can hold size",
+ (buf->p->len >= size));
+ buf->ptr = buf->p;
+ return buf->p->payload;
+}
+
+/**
+ * @ingroup netbuf
+ * Free the packet buffer included in a netbuf
+ *
+ * @param buf pointer to the netbuf which contains the packet buffer to free
+ */
+void
+netbuf_free(struct netbuf *buf)
+{
+ LWIP_ERROR("netbuf_free: invalid buf", (buf != NULL), return;);
+ if (buf->p != NULL) {
+ pbuf_free(buf->p);
+ }
+ buf->p = buf->ptr = NULL;
+}
+
+/**
+ * @ingroup netbuf
+ * Let a netbuf reference existing (non-volatile) data.
+ *
+ * @param buf netbuf which should reference the data
+ * @param dataptr pointer to the data to reference
+ * @param size size of the data
+ * @return ERR_OK if data is referenced
+ * ERR_MEM if data couldn't be referenced due to lack of memory
+ */
+err_t
+netbuf_ref(struct netbuf *buf, const void *dataptr, u16_t size)
+{
+ LWIP_ERROR("netbuf_ref: invalid buf", (buf != NULL), return ERR_ARG;);
+ if (buf->p != NULL) {
+ pbuf_free(buf->p);
+ }
+ buf->p = pbuf_alloc(PBUF_TRANSPORT, 0, PBUF_REF);
+ if (buf->p == NULL) {
+ buf->ptr = NULL;
+ return ERR_MEM;
+ }
+ ((struct pbuf_rom*)buf->p)->payload = dataptr;
+ buf->p->len = buf->p->tot_len = size;
+ buf->ptr = buf->p;
+ return ERR_OK;
+}
+
+/**
+ * @ingroup netbuf
+ * Chain one netbuf to another (@see pbuf_chain)
+ *
+ * @param head the first netbuf
+ * @param tail netbuf to chain after head, freed by this function, may not be reference after returning
+ */
+void
+netbuf_chain(struct netbuf *head, struct netbuf *tail)
+{
+ LWIP_ERROR("netbuf_chain: invalid head", (head != NULL), return;);
+ LWIP_ERROR("netbuf_chain: invalid tail", (tail != NULL), return;);
+ pbuf_cat(head->p, tail->p);
+ head->ptr = head->p;
+ memp_free(MEMP_NETBUF, tail);
+}
+
+/**
+ * @ingroup netbuf
+ * Get the data pointer and length of the data inside a netbuf.
+ *
+ * @param buf netbuf to get the data from
+ * @param dataptr pointer to a void pointer where to store the data pointer
+ * @param len pointer to an u16_t where the length of the data is stored
+ * @return ERR_OK if the information was retrieved,
+ * ERR_BUF on error.
+ */
+err_t
+netbuf_data(struct netbuf *buf, void **dataptr, u16_t *len)
+{
+ LWIP_ERROR("netbuf_data: invalid buf", (buf != NULL), return ERR_ARG;);
+ LWIP_ERROR("netbuf_data: invalid dataptr", (dataptr != NULL), return ERR_ARG;);
+ LWIP_ERROR("netbuf_data: invalid len", (len != NULL), return ERR_ARG;);
+
+ if (buf->ptr == NULL) {
+ return ERR_BUF;
+ }
+ *dataptr = buf->ptr->payload;
+ *len = buf->ptr->len;
+ return ERR_OK;
+}
+
+/**
+ * @ingroup netbuf
+ * Move the current data pointer of a packet buffer contained in a netbuf
+ * to the next part.
+ * The packet buffer itself is not modified.
+ *
+ * @param buf the netbuf to modify
+ * @return -1 if there is no next part
+ * 1 if moved to the next part but now there is no next part
+ * 0 if moved to the next part and there are still more parts
+ */
+s8_t
+netbuf_next(struct netbuf *buf)
+{
+ LWIP_ERROR("netbuf_next: invalid buf", (buf != NULL), return -1;);
+ if (buf->ptr->next == NULL) {
+ return -1;
+ }
+ buf->ptr = buf->ptr->next;
+ if (buf->ptr->next == NULL) {
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * @ingroup netbuf
+ * Move the current data pointer of a packet buffer contained in a netbuf
+ * to the beginning of the packet.
+ * The packet buffer itself is not modified.
+ *
+ * @param buf the netbuf to modify
+ */
+void
+netbuf_first(struct netbuf *buf)
+{
+ LWIP_ERROR("netbuf_first: invalid buf", (buf != NULL), return;);
+ buf->ptr = buf->p;
+}
+
+#endif /* LWIP_NETCONN */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/api/netdb.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/api/netdb.c
new file mode 100644
index 0000000..ccd9586
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/api/netdb.c
@@ -0,0 +1,413 @@
+/**
+ * @file
+ * API functions for name resolving
+ *
+ * @defgroup netdbapi NETDB API
+ * @ingroup socket
+ */
+
+/*
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Simon Goldschmidt
+ *
+ */
+
+#include "lwip/netdb.h"
+
+#if LWIP_DNS && LWIP_SOCKET
+
+#include "lwip/err.h"
+#include "lwip/mem.h"
+#include "lwip/memp.h"
+#include "lwip/ip_addr.h"
+#include "lwip/api.h"
+#include "lwip/dns.h"
+
+#include <string.h> /* memset */
+#include <stdlib.h> /* atoi */
+
+/** helper struct for gethostbyname_r to access the char* buffer */
+struct gethostbyname_r_helper {
+ ip_addr_t *addr_list[2];
+ ip_addr_t addr;
+ char *aliases;
+};
+
+/** h_errno is exported in netdb.h for access by applications. */
+#if LWIP_DNS_API_DECLARE_H_ERRNO
+int h_errno;
+#endif /* LWIP_DNS_API_DECLARE_H_ERRNO */
+
+/** define "hostent" variables storage: 0 if we use a static (but unprotected)
+ * set of variables for lwip_gethostbyname, 1 if we use a local storage */
+#ifndef LWIP_DNS_API_HOSTENT_STORAGE
+#define LWIP_DNS_API_HOSTENT_STORAGE 0
+#endif
+
+/** define "hostent" variables storage */
+#if LWIP_DNS_API_HOSTENT_STORAGE
+#define HOSTENT_STORAGE
+#else
+#define HOSTENT_STORAGE static
+#endif /* LWIP_DNS_API_STATIC_HOSTENT */
+
+/**
+ * Returns an entry containing addresses of address family AF_INET
+ * for the host with name name.
+ * Due to dns_gethostbyname limitations, only one address is returned.
+ *
+ * @param name the hostname to resolve
+ * @return an entry containing addresses of address family AF_INET
+ * for the host with name name
+ */
+struct hostent*
+lwip_gethostbyname(const char *name)
+{
+ err_t err;
+ ip_addr_t addr;
+
+ /* buffer variables for lwip_gethostbyname() */
+ HOSTENT_STORAGE struct hostent s_hostent;
+ HOSTENT_STORAGE char *s_aliases;
+ HOSTENT_STORAGE ip_addr_t s_hostent_addr;
+ HOSTENT_STORAGE ip_addr_t *s_phostent_addr[2];
+ HOSTENT_STORAGE char s_hostname[DNS_MAX_NAME_LENGTH + 1];
+
+ /* query host IP address */
+ err = netconn_gethostbyname(name, &addr);
+ if (err != ERR_OK) {
+ LWIP_DEBUGF(DNS_DEBUG, ("lwip_gethostbyname(%s) failed, err=%d\n", name, err));
+ h_errno = HOST_NOT_FOUND;
+ return NULL;
+ }
+
+ /* fill hostent */
+ s_hostent_addr = addr;
+ s_phostent_addr[0] = &s_hostent_addr;
+ s_phostent_addr[1] = NULL;
+ strncpy(s_hostname, name, DNS_MAX_NAME_LENGTH);
+ s_hostname[DNS_MAX_NAME_LENGTH] = 0;
+ s_hostent.h_name = s_hostname;
+ s_aliases = NULL;
+ s_hostent.h_aliases = &s_aliases;
+ s_hostent.h_addrtype = AF_INET;
+ s_hostent.h_length = sizeof(ip_addr_t);
+ s_hostent.h_addr_list = (char**)&s_phostent_addr;
+
+#if DNS_DEBUG
+ /* dump hostent */
+ LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_name == %s\n", s_hostent.h_name));
+ LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_aliases == %p\n", (void*)s_hostent.h_aliases));
+ /* h_aliases are always empty */
+ LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addrtype == %d\n", s_hostent.h_addrtype));
+ LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_length == %d\n", s_hostent.h_length));
+ LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addr_list == %p\n", (void*)s_hostent.h_addr_list));
+ if (s_hostent.h_addr_list != NULL) {
+ u8_t idx;
+ for (idx=0; s_hostent.h_addr_list[idx]; idx++) {
+ LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addr_list[%i] == %p\n", idx, s_hostent.h_addr_list[idx]));
+ LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addr_list[%i]-> == %s\n", idx, ipaddr_ntoa((ip_addr_t*)s_hostent.h_addr_list[idx])));
+ }
+ }
+#endif /* DNS_DEBUG */
+
+#if LWIP_DNS_API_HOSTENT_STORAGE
+ /* this function should return the "per-thread" hostent after copy from s_hostent */
+ return sys_thread_hostent(&s_hostent);
+#else
+ return &s_hostent;
+#endif /* LWIP_DNS_API_HOSTENT_STORAGE */
+}
+
+/**
+ * Thread-safe variant of lwip_gethostbyname: instead of using a static
+ * buffer, this function takes buffer and errno pointers as arguments
+ * and uses these for the result.
+ *
+ * @param name the hostname to resolve
+ * @param ret pre-allocated struct where to store the result
+ * @param buf pre-allocated buffer where to store additional data
+ * @param buflen the size of buf
+ * @param result pointer to a hostent pointer that is set to ret on success
+ * and set to zero on error
+ * @param h_errnop pointer to an int where to store errors (instead of modifying
+ * the global h_errno)
+ * @return 0 on success, non-zero on error, additional error information
+ * is stored in *h_errnop instead of h_errno to be thread-safe
+ */
+int
+lwip_gethostbyname_r(const char *name, struct hostent *ret, char *buf,
+ size_t buflen, struct hostent **result, int *h_errnop)
+{
+ err_t err;
+ struct gethostbyname_r_helper *h;
+ char *hostname;
+ size_t namelen;
+ int lh_errno;
+
+ if (h_errnop == NULL) {
+ /* ensure h_errnop is never NULL */
+ h_errnop = &lh_errno;
+ }
+
+ if (result == NULL) {
+ /* not all arguments given */
+ *h_errnop = EINVAL;
+ return -1;
+ }
+ /* first thing to do: set *result to nothing */
+ *result = NULL;
+ if ((name == NULL) || (ret == NULL) || (buf == NULL)) {
+ /* not all arguments given */
+ *h_errnop = EINVAL;
+ return -1;
+ }
+
+ namelen = strlen(name);
+ if (buflen < (sizeof(struct gethostbyname_r_helper) + namelen + 1 + (MEM_ALIGNMENT - 1))) {
+ /* buf can't hold the data needed + a copy of name */
+ *h_errnop = ERANGE;
+ return -1;
+ }
+
+ h = (struct gethostbyname_r_helper*)LWIP_MEM_ALIGN(buf);
+ hostname = ((char*)h) + sizeof(struct gethostbyname_r_helper);
+
+ /* query host IP address */
+ err = netconn_gethostbyname(name, &h->addr);
+ if (err != ERR_OK) {
+ LWIP_DEBUGF(DNS_DEBUG, ("lwip_gethostbyname(%s) failed, err=%d\n", name, err));
+ *h_errnop = HOST_NOT_FOUND;
+ return -1;
+ }
+
+ /* copy the hostname into buf */
+ MEMCPY(hostname, name, namelen);
+ hostname[namelen] = 0;
+
+ /* fill hostent */
+ h->addr_list[0] = &h->addr;
+ h->addr_list[1] = NULL;
+ h->aliases = NULL;
+ ret->h_name = hostname;
+ ret->h_aliases = &h->aliases;
+ ret->h_addrtype = AF_INET;
+ ret->h_length = sizeof(ip_addr_t);
+ ret->h_addr_list = (char**)&h->addr_list;
+
+ /* set result != NULL */
+ *result = ret;
+
+ /* return success */
+ return 0;
+}
+
+/**
+ * Frees one or more addrinfo structures returned by getaddrinfo(), along with
+ * any additional storage associated with those structures. If the ai_next field
+ * of the structure is not null, the entire list of structures is freed.
+ *
+ * @param ai struct addrinfo to free
+ */
+void
+lwip_freeaddrinfo(struct addrinfo *ai)
+{
+ struct addrinfo *next;
+
+ while (ai != NULL) {
+ next = ai->ai_next;
+ memp_free(MEMP_NETDB, ai);
+ ai = next;
+ }
+}
+
+/**
+ * Translates the name of a service location (for example, a host name) and/or
+ * a service name and returns a set of socket addresses and associated
+ * information to be used in creating a socket with which to address the
+ * specified service.
+ * Memory for the result is allocated internally and must be freed by calling
+ * lwip_freeaddrinfo()!
+ *
+ * Due to a limitation in dns_gethostbyname, only the first address of a
+ * host is returned.
+ * Also, service names are not supported (only port numbers)!
+ *
+ * @param nodename descriptive name or address string of the host
+ * (may be NULL -> local address)
+ * @param servname port number as string of NULL
+ * @param hints structure containing input values that set socktype and protocol
+ * @param res pointer to a pointer where to store the result (set to NULL on failure)
+ * @return 0 on success, non-zero on failure
+ *
+ * @todo: implement AI_V4MAPPED, AI_ADDRCONFIG
+ */
+int
+lwip_getaddrinfo(const char *nodename, const char *servname,
+ const struct addrinfo *hints, struct addrinfo **res)
+{
+ err_t err;
+ ip_addr_t addr;
+ struct addrinfo *ai;
+ struct sockaddr_storage *sa = NULL;
+ int port_nr = 0;
+ size_t total_size;
+ size_t namelen = 0;
+ int ai_family;
+
+ if (res == NULL) {
+ return EAI_FAIL;
+ }
+ *res = NULL;
+ if ((nodename == NULL) && (servname == NULL)) {
+ return EAI_NONAME;
+ }
+
+ if (hints != NULL) {
+ ai_family = hints->ai_family;
+ if ((ai_family != AF_UNSPEC)
+#if LWIP_IPV4
+ && (ai_family != AF_INET)
+#endif /* LWIP_IPV4 */
+#if LWIP_IPV6
+ && (ai_family != AF_INET6)
+#endif /* LWIP_IPV6 */
+ ) {
+ return EAI_FAMILY;
+ }
+ } else {
+ ai_family = AF_UNSPEC;
+ }
+
+ if (servname != NULL) {
+ /* service name specified: convert to port number
+ * @todo?: currently, only ASCII integers (port numbers) are supported (AI_NUMERICSERV)! */
+ port_nr = atoi(servname);
+ if ((port_nr <= 0) || (port_nr > 0xffff)) {
+ return EAI_SERVICE;
+ }
+ }
+
+ if (nodename != NULL) {
+ /* service location specified, try to resolve */
+ if ((hints != NULL) && (hints->ai_flags & AI_NUMERICHOST)) {
+ /* no DNS lookup, just parse for an address string */
+ if (!ipaddr_aton(nodename, &addr)) {
+ return EAI_NONAME;
+ }
+#if LWIP_IPV4 && LWIP_IPV6
+ if ((IP_IS_V6_VAL(addr) && ai_family == AF_INET) ||
+ (IP_IS_V4_VAL(addr) && ai_family == AF_INET6)) {
+ return EAI_NONAME;
+ }
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+ } else {
+#if LWIP_IPV4 && LWIP_IPV6
+ /* AF_UNSPEC: prefer IPv4 */
+ u8_t type = NETCONN_DNS_IPV4_IPV6;
+ if (ai_family == AF_INET) {
+ type = NETCONN_DNS_IPV4;
+ } else if (ai_family == AF_INET6) {
+ type = NETCONN_DNS_IPV6;
+ }
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+ err = netconn_gethostbyname_addrtype(nodename, &addr, type);
+ if (err != ERR_OK) {
+ return EAI_FAIL;
+ }
+ }
+ } else {
+ /* service location specified, use loopback address */
+ if ((hints != NULL) && (hints->ai_flags & AI_PASSIVE)) {
+ ip_addr_set_any(ai_family == AF_INET6, &addr);
+ } else {
+ ip_addr_set_loopback(ai_family == AF_INET6, &addr);
+ }
+ }
+
+ total_size = sizeof(struct addrinfo) + sizeof(struct sockaddr_storage);
+ if (nodename != NULL) {
+ namelen = strlen(nodename);
+ if (namelen > DNS_MAX_NAME_LENGTH) {
+ /* invalid name length */
+ return EAI_FAIL;
+ }
+ LWIP_ASSERT("namelen is too long", total_size + namelen + 1 > total_size);
+ total_size += namelen + 1;
+ }
+ /* If this fails, please report to lwip-devel! :-) */
+ LWIP_ASSERT("total_size <= NETDB_ELEM_SIZE: please report this!",
+ total_size <= NETDB_ELEM_SIZE);
+ ai = (struct addrinfo *)memp_malloc(MEMP_NETDB);
+ if (ai == NULL) {
+ return EAI_MEMORY;
+ }
+ memset(ai, 0, total_size);
+ /* cast through void* to get rid of alignment warnings */
+ sa = (struct sockaddr_storage *)(void*)((u8_t*)ai + sizeof(struct addrinfo));
+ if (IP_IS_V6_VAL(addr)) {
+#if LWIP_IPV6
+ struct sockaddr_in6 *sa6 = (struct sockaddr_in6*)sa;
+ /* set up sockaddr */
+ inet6_addr_from_ip6addr(&sa6->sin6_addr, ip_2_ip6(&addr));
+ sa6->sin6_family = AF_INET6;
+ sa6->sin6_len = sizeof(struct sockaddr_in6);
+ sa6->sin6_port = lwip_htons((u16_t)port_nr);
+ ai->ai_family = AF_INET6;
+#endif /* LWIP_IPV6 */
+ } else {
+#if LWIP_IPV4
+ struct sockaddr_in *sa4 = (struct sockaddr_in*)sa;
+ /* set up sockaddr */
+ inet_addr_from_ip4addr(&sa4->sin_addr, ip_2_ip4(&addr));
+ sa4->sin_family = AF_INET;
+ sa4->sin_len = sizeof(struct sockaddr_in);
+ sa4->sin_port = lwip_htons((u16_t)port_nr);
+ ai->ai_family = AF_INET;
+#endif /* LWIP_IPV4 */
+ }
+
+ /* set up addrinfo */
+ if (hints != NULL) {
+ /* copy socktype & protocol from hints if specified */
+ ai->ai_socktype = hints->ai_socktype;
+ ai->ai_protocol = hints->ai_protocol;
+ }
+ if (nodename != NULL) {
+ /* copy nodename to canonname if specified */
+ ai->ai_canonname = ((char*)ai + sizeof(struct addrinfo) + sizeof(struct sockaddr_storage));
+ MEMCPY(ai->ai_canonname, nodename, namelen);
+ ai->ai_canonname[namelen] = 0;
+ }
+ ai->ai_addrlen = sizeof(struct sockaddr_storage);
+ ai->ai_addr = (struct sockaddr*)sa;
+
+ *res = ai;
+
+ return 0;
+}
+
+#endif /* LWIP_DNS && LWIP_SOCKET */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/api/netifapi.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/api/netifapi.c
new file mode 100644
index 0000000..fef05a3
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/api/netifapi.c
@@ -0,0 +1,221 @@
+/**
+ * @file
+ * Network Interface Sequential API module
+ *
+ * @defgroup netifapi NETIF API
+ * @ingroup sequential_api
+ * Thread-safe functions to be called from non-TCPIP threads
+ *
+ * @defgroup netifapi_netif NETIF related
+ * @ingroup netifapi
+ * To be called from non-TCPIP threads
+ */
+
+/*
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_NETIF_API /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/netifapi.h"
+#include "lwip/memp.h"
+#include "lwip/priv/tcpip_priv.h"
+
+#define NETIFAPI_VAR_REF(name) API_VAR_REF(name)
+#define NETIFAPI_VAR_DECLARE(name) API_VAR_DECLARE(struct netifapi_msg, name)
+#define NETIFAPI_VAR_ALLOC(name) API_VAR_ALLOC(struct netifapi_msg, MEMP_NETIFAPI_MSG, name, ERR_MEM)
+#define NETIFAPI_VAR_FREE(name) API_VAR_FREE(MEMP_NETIFAPI_MSG, name)
+
+/**
+ * Call netif_add() inside the tcpip_thread context.
+ */
+static err_t
+netifapi_do_netif_add(struct tcpip_api_call_data *m)
+{
+ /* cast through void* to silence alignment warnings.
+ * We know it works because the structs have been instantiated as struct netifapi_msg */
+ struct netifapi_msg *msg = (struct netifapi_msg*)(void*)m;
+
+ if (!netif_add( msg->netif,
+#if LWIP_IPV4
+ API_EXPR_REF(msg->msg.add.ipaddr),
+ API_EXPR_REF(msg->msg.add.netmask),
+ API_EXPR_REF(msg->msg.add.gw),
+#endif /* LWIP_IPV4 */
+ msg->msg.add.state,
+ msg->msg.add.init,
+ msg->msg.add.input)) {
+ return ERR_IF;
+ } else {
+ return ERR_OK;
+ }
+}
+
+#if LWIP_IPV4
+/**
+ * Call netif_set_addr() inside the tcpip_thread context.
+ */
+static err_t
+netifapi_do_netif_set_addr(struct tcpip_api_call_data *m)
+{
+ /* cast through void* to silence alignment warnings.
+ * We know it works because the structs have been instantiated as struct netifapi_msg */
+ struct netifapi_msg *msg = (struct netifapi_msg*)(void*)m;
+
+ netif_set_addr( msg->netif,
+ API_EXPR_REF(msg->msg.add.ipaddr),
+ API_EXPR_REF(msg->msg.add.netmask),
+ API_EXPR_REF(msg->msg.add.gw));
+ return ERR_OK;
+}
+#endif /* LWIP_IPV4 */
+
+/**
+ * Call the "errtfunc" (or the "voidfunc" if "errtfunc" is NULL) inside the
+ * tcpip_thread context.
+ */
+static err_t
+netifapi_do_netif_common(struct tcpip_api_call_data *m)
+{
+ /* cast through void* to silence alignment warnings.
+ * We know it works because the structs have been instantiated as struct netifapi_msg */
+ struct netifapi_msg *msg = (struct netifapi_msg*)(void*)m;
+
+ if (msg->msg.common.errtfunc != NULL) {
+ return msg->msg.common.errtfunc(msg->netif);
+ } else {
+ msg->msg.common.voidfunc(msg->netif);
+ return ERR_OK;
+ }
+}
+
+/**
+ * @ingroup netifapi_netif
+ * Call netif_add() in a thread-safe way by running that function inside the
+ * tcpip_thread context.
+ *
+ * @note for params @see netif_add()
+ */
+err_t
+netifapi_netif_add(struct netif *netif,
+#if LWIP_IPV4
+ const ip4_addr_t *ipaddr, const ip4_addr_t *netmask, const ip4_addr_t *gw,
+#endif /* LWIP_IPV4 */
+ void *state, netif_init_fn init, netif_input_fn input)
+{
+ err_t err;
+ NETIFAPI_VAR_DECLARE(msg);
+ NETIFAPI_VAR_ALLOC(msg);
+
+#if LWIP_IPV4
+ if (ipaddr == NULL) {
+ ipaddr = IP4_ADDR_ANY4;
+ }
+ if (netmask == NULL) {
+ netmask = IP4_ADDR_ANY4;
+ }
+ if (gw == NULL) {
+ gw = IP4_ADDR_ANY4;
+ }
+#endif /* LWIP_IPV4 */
+
+ NETIFAPI_VAR_REF(msg).netif = netif;
+#if LWIP_IPV4
+ NETIFAPI_VAR_REF(msg).msg.add.ipaddr = NETIFAPI_VAR_REF(ipaddr);
+ NETIFAPI_VAR_REF(msg).msg.add.netmask = NETIFAPI_VAR_REF(netmask);
+ NETIFAPI_VAR_REF(msg).msg.add.gw = NETIFAPI_VAR_REF(gw);
+#endif /* LWIP_IPV4 */
+ NETIFAPI_VAR_REF(msg).msg.add.state = state;
+ NETIFAPI_VAR_REF(msg).msg.add.init = init;
+ NETIFAPI_VAR_REF(msg).msg.add.input = input;
+ err = tcpip_api_call(netifapi_do_netif_add, &API_VAR_REF(msg).call);
+ NETIFAPI_VAR_FREE(msg);
+ return err;
+}
+
+#if LWIP_IPV4
+/**
+ * @ingroup netifapi_netif
+ * Call netif_set_addr() in a thread-safe way by running that function inside the
+ * tcpip_thread context.
+ *
+ * @note for params @see netif_set_addr()
+ */
+err_t
+netifapi_netif_set_addr(struct netif *netif,
+ const ip4_addr_t *ipaddr,
+ const ip4_addr_t *netmask,
+ const ip4_addr_t *gw)
+{
+ err_t err;
+ NETIFAPI_VAR_DECLARE(msg);
+ NETIFAPI_VAR_ALLOC(msg);
+
+ if (ipaddr == NULL) {
+ ipaddr = IP4_ADDR_ANY4;
+ }
+ if (netmask == NULL) {
+ netmask = IP4_ADDR_ANY4;
+ }
+ if (gw == NULL) {
+ gw = IP4_ADDR_ANY4;
+ }
+
+ NETIFAPI_VAR_REF(msg).netif = netif;
+ NETIFAPI_VAR_REF(msg).msg.add.ipaddr = NETIFAPI_VAR_REF(ipaddr);
+ NETIFAPI_VAR_REF(msg).msg.add.netmask = NETIFAPI_VAR_REF(netmask);
+ NETIFAPI_VAR_REF(msg).msg.add.gw = NETIFAPI_VAR_REF(gw);
+ err = tcpip_api_call(netifapi_do_netif_set_addr, &API_VAR_REF(msg).call);
+ NETIFAPI_VAR_FREE(msg);
+ return err;
+}
+#endif /* LWIP_IPV4 */
+
+/**
+ * call the "errtfunc" (or the "voidfunc" if "errtfunc" is NULL) in a thread-safe
+ * way by running that function inside the tcpip_thread context.
+ *
+ * @note use only for functions where there is only "netif" parameter.
+ */
+err_t
+netifapi_netif_common(struct netif *netif, netifapi_void_fn voidfunc,
+ netifapi_errt_fn errtfunc)
+{
+ err_t err;
+ NETIFAPI_VAR_DECLARE(msg);
+ NETIFAPI_VAR_ALLOC(msg);
+
+ NETIFAPI_VAR_REF(msg).netif = netif;
+ NETIFAPI_VAR_REF(msg).msg.common.voidfunc = voidfunc;
+ NETIFAPI_VAR_REF(msg).msg.common.errtfunc = errtfunc;
+ err = tcpip_api_call(netifapi_do_netif_common, &API_VAR_REF(msg).call);
+ NETIFAPI_VAR_FREE(msg);
+ return err;
+}
+
+#endif /* LWIP_NETIF_API */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/api/pppapi.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/api/pppapi.c
new file mode 100644
index 0000000..5ba190e
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/api/pppapi.c
@@ -0,0 +1,416 @@
+/**
+ * @file
+ * Point To Point Protocol Sequential API module
+ *
+ */
+
+/*
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_PPP_API /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/pppapi.h"
+#include "lwip/tcpip.h"
+#include "netif/ppp/pppoe.h"
+#include "netif/ppp/pppol2tp.h"
+#include "netif/ppp/pppos.h"
+
+/**
+ * Call ppp_set_default() inside the tcpip_thread context.
+ */
+static void
+pppapi_do_ppp_set_default(struct pppapi_msg_msg *msg)
+{
+ ppp_set_default(msg->ppp);
+ TCPIP_PPPAPI_ACK(msg);
+}
+
+/**
+ * Call ppp_set_default() in a thread-safe way by running that function inside the
+ * tcpip_thread context.
+ */
+void
+pppapi_set_default(ppp_pcb *pcb)
+{
+ struct pppapi_msg msg;
+ msg.function = pppapi_do_ppp_set_default;
+ msg.msg.ppp = pcb;
+ TCPIP_PPPAPI(&msg);
+}
+
+
+/**
+ * Call ppp_set_auth() inside the tcpip_thread context.
+ */
+static void
+pppapi_do_ppp_set_auth(struct pppapi_msg_msg *msg)
+{
+ ppp_set_auth(msg->ppp, msg->msg.setauth.authtype,
+ msg->msg.setauth.user, msg->msg.setauth.passwd);
+ TCPIP_PPPAPI_ACK(msg);
+}
+
+/**
+ * Call ppp_set_auth() in a thread-safe way by running that function inside the
+ * tcpip_thread context.
+ */
+void
+pppapi_set_auth(ppp_pcb *pcb, u8_t authtype, const char *user, const char *passwd)
+{
+ struct pppapi_msg msg;
+ msg.function = pppapi_do_ppp_set_auth;
+ msg.msg.ppp = pcb;
+ msg.msg.msg.setauth.authtype = authtype;
+ msg.msg.msg.setauth.user = user;
+ msg.msg.msg.setauth.passwd = passwd;
+ TCPIP_PPPAPI(&msg);
+}
+
+
+#if PPP_NOTIFY_PHASE
+/**
+ * Call ppp_set_notify_phase_callback() inside the tcpip_thread context.
+ */
+static void
+pppapi_do_ppp_set_notify_phase_callback(struct pppapi_msg_msg *msg)
+{
+ ppp_set_notify_phase_callback(msg->ppp, msg->msg.setnotifyphasecb.notify_phase_cb);
+ TCPIP_PPPAPI_ACK(msg);
+}
+
+/**
+ * Call ppp_set_notify_phase_callback() in a thread-safe way by running that function inside the
+ * tcpip_thread context.
+ */
+void
+pppapi_set_notify_phase_callback(ppp_pcb *pcb, ppp_notify_phase_cb_fn notify_phase_cb)
+{
+ struct pppapi_msg msg;
+ msg.function = pppapi_do_ppp_set_notify_phase_callback;
+ msg.msg.ppp = pcb;
+ msg.msg.msg.setnotifyphasecb.notify_phase_cb = notify_phase_cb;
+ TCPIP_PPPAPI(&msg);
+}
+#endif /* PPP_NOTIFY_PHASE */
+
+
+#if PPPOS_SUPPORT
+/**
+ * Call pppos_create() inside the tcpip_thread context.
+ */
+static void
+pppapi_do_pppos_create(struct pppapi_msg_msg *msg)
+{
+ msg->ppp = pppos_create(msg->msg.serialcreate.pppif, msg->msg.serialcreate.fd,
+ msg->msg.serialcreate.link_status_cb, msg->msg.serialcreate.ctx_cb);
+ TCPIP_PPPAPI_ACK(msg);
+}
+
+/**
+ * Call pppos_create() in a thread-safe way by running that function inside the
+ * tcpip_thread context.
+ */
+ppp_pcb*
+pppapi_pppos_create(struct netif *pppif, sio_fd_t fd, ppp_link_status_cb_fn link_status_cb,
+ void *ctx_cb)
+{
+ struct pppapi_msg msg;
+ msg.function = pppapi_do_pppos_create;
+ msg.msg.msg.serialcreate.pppif = pppif;
+ msg.msg.msg.serialcreate.fd = fd;
+ msg.msg.msg.serialcreate.link_status_cb = link_status_cb;
+ msg.msg.msg.serialcreate.ctx_cb = ctx_cb;
+ TCPIP_PPPAPI(&msg);
+ return msg.msg.ppp;
+}
+#endif /* PPPOS_SUPPORT */
+
+
+#if PPPOE_SUPPORT
+/**
+ * Call pppoe_create() inside the tcpip_thread context.
+ */
+static void
+pppapi_do_pppoe_create(struct pppapi_msg_msg *msg)
+{
+
+ msg->ppp = pppoe_create(msg->msg.ethernetcreate.pppif, msg->msg.ethernetcreate.ethif,
+ msg->msg.ethernetcreate.service_name, msg->msg.ethernetcreate.concentrator_name,
+ msg->msg.ethernetcreate.link_status_cb, msg->msg.ethernetcreate.ctx_cb);
+ TCPIP_PPPAPI_ACK(msg);
+}
+
+/**
+ * Call pppoe_create() in a thread-safe way by running that function inside the
+ * tcpip_thread context.
+ */
+ppp_pcb*
+pppapi_pppoe_create(struct netif *pppif, struct netif *ethif, const char *service_name,
+ const char *concentrator_name, ppp_link_status_cb_fn link_status_cb,
+ void *ctx_cb)
+{
+ struct pppapi_msg msg;
+ msg.function = pppapi_do_pppoe_create;
+ msg.msg.msg.ethernetcreate.pppif = pppif;
+ msg.msg.msg.ethernetcreate.ethif = ethif;
+ msg.msg.msg.ethernetcreate.service_name = service_name;
+ msg.msg.msg.ethernetcreate.concentrator_name = concentrator_name;
+ msg.msg.msg.ethernetcreate.link_status_cb = link_status_cb;
+ msg.msg.msg.ethernetcreate.ctx_cb = ctx_cb;
+ TCPIP_PPPAPI(&msg);
+ return msg.msg.ppp;
+}
+#endif /* PPPOE_SUPPORT */
+
+
+#if PPPOL2TP_SUPPORT
+/**
+ * Call pppol2tp_create() inside the tcpip_thread context.
+ */
+static void
+pppapi_do_pppol2tp_create(struct pppapi_msg_msg *msg)
+{
+ msg->ppp = pppol2tp_create(msg->msg.l2tpcreate.pppif,
+ msg->msg.l2tpcreate.netif, msg->msg.l2tpcreate.ipaddr, msg->msg.l2tpcreate.port,
+#if PPPOL2TP_AUTH_SUPPORT
+ msg->msg.l2tpcreate.secret,
+ msg->msg.l2tpcreate.secret_len,
+#else /* PPPOL2TP_AUTH_SUPPORT */
+ NULL,
+#endif /* PPPOL2TP_AUTH_SUPPORT */
+ msg->msg.l2tpcreate.link_status_cb, msg->msg.l2tpcreate.ctx_cb);
+ TCPIP_PPPAPI_ACK(msg);
+}
+
+/**
+ * Call pppol2tp_create() in a thread-safe way by running that function inside the
+ * tcpip_thread context.
+ */
+ppp_pcb*
+pppapi_pppol2tp_create(struct netif *pppif, struct netif *netif, ip_addr_t *ipaddr, u16_t port,
+ u8_t *secret, u8_t secret_len,
+ ppp_link_status_cb_fn link_status_cb, void *ctx_cb)
+{
+ struct pppapi_msg msg;
+ msg.function = pppapi_do_pppol2tp_create;
+ msg.msg.msg.l2tpcreate.pppif = pppif;
+ msg.msg.msg.l2tpcreate.netif = netif;
+ msg.msg.msg.l2tpcreate.ipaddr = ipaddr;
+ msg.msg.msg.l2tpcreate.port = port;
+#if PPPOL2TP_AUTH_SUPPORT
+ msg.msg.msg.l2tpcreate.secret = secret;
+ msg.msg.msg.l2tpcreate.secret_len = secret_len;
+#endif /* PPPOL2TP_AUTH_SUPPORT */
+ msg.msg.msg.l2tpcreate.link_status_cb = link_status_cb;
+ msg.msg.msg.l2tpcreate.ctx_cb = ctx_cb;
+ TCPIP_PPPAPI(&msg);
+ return msg.msg.ppp;
+}
+
+
+#if LWIP_IPV6
+/**
+ * Call pppol2tp_create_ip6() inside the tcpip_thread context.
+ */
+static void
+pppapi_do_pppol2tp_create_ip6(struct pppapi_msg_msg *msg)
+{
+ msg->ppp = pppol2tp_create_ip6(msg->msg.l2tpcreateip6.pppif,
+ msg->msg.l2tpcreateip6.netif, msg->msg.l2tpcreateip6.ip6addr, msg->msg.l2tpcreateip6.port,
+#if PPPOL2TP_AUTH_SUPPORT
+ msg->msg.l2tpcreateip6.secret,
+ msg->msg.l2tpcreateip6.secret_len,
+#else /* PPPOL2TP_AUTH_SUPPORT */
+ NULL,
+#endif /* PPPOL2TP_AUTH_SUPPORT */
+ msg->msg.l2tpcreateip6.link_status_cb, msg->msg.l2tpcreateip6.ctx_cb);
+ TCPIP_PPPAPI_ACK(msg);
+}
+
+/**
+ * Call pppol2tp_create_ip6() in a thread-safe way by running that function inside the
+ * tcpip_thread context.
+ */
+ppp_pcb*
+pppapi_pppol2tp_create_ip6(struct netif *pppif, struct netif *netif, ip6_addr_t *ip6addr, u16_t port,
+ u8_t *secret, u8_t secret_len,
+ ppp_link_status_cb_fn link_status_cb, void *ctx_cb)
+{
+ struct pppapi_msg msg;
+ msg.function = pppapi_do_pppol2tp_create_ip6;
+ msg.msg.msg.l2tpcreateip6.pppif = pppif;
+ msg.msg.msg.l2tpcreateip6.netif = netif;
+ msg.msg.msg.l2tpcreateip6.ip6addr = ip6addr;
+ msg.msg.msg.l2tpcreateip6.port = port;
+#if PPPOL2TP_AUTH_SUPPORT
+ msg.msg.msg.l2tpcreateip6.secret = secret;
+ msg.msg.msg.l2tpcreateip6.secret_len = secret_len;
+#endif /* PPPOL2TP_AUTH_SUPPORT */
+ msg.msg.msg.l2tpcreateip6.link_status_cb = link_status_cb;
+ msg.msg.msg.l2tpcreateip6.ctx_cb = ctx_cb;
+ TCPIP_PPPAPI(&msg);
+ return msg.msg.ppp;
+}
+#endif /* LWIP_IPV6 */
+#endif /* PPPOL2TP_SUPPORT */
+
+
+/**
+ * Call ppp_connect() inside the tcpip_thread context.
+ */
+static void
+pppapi_do_ppp_connect(struct pppapi_msg_msg *msg)
+{
+ msg->err = ppp_connect(msg->ppp, msg->msg.connect.holdoff);
+ TCPIP_PPPAPI_ACK(msg);
+}
+
+/**
+ * Call ppp_connect() in a thread-safe way by running that function inside the
+ * tcpip_thread context.
+ */
+err_t
+pppapi_connect(ppp_pcb *pcb, u16_t holdoff)
+{
+ struct pppapi_msg msg;
+ msg.function = pppapi_do_ppp_connect;
+ msg.msg.ppp = pcb;
+ msg.msg.msg.connect.holdoff = holdoff;
+ TCPIP_PPPAPI(&msg);
+ return msg.msg.err;
+}
+
+
+#if PPP_SERVER
+/**
+ * Call ppp_listen() inside the tcpip_thread context.
+ */
+static void
+pppapi_do_ppp_listen(struct pppapi_msg_msg *msg)
+{
+ msg->err = ppp_listen(msg->ppp, msg->msg.listen.addrs);
+ TCPIP_PPPAPI_ACK(msg);
+}
+
+/**
+ * Call ppp_listen() in a thread-safe way by running that function inside the
+ * tcpip_thread context.
+ */
+err_t
+pppapi_listen(ppp_pcb *pcb, struct ppp_addrs *addrs)
+{
+ struct pppapi_msg msg;
+ msg.function = pppapi_do_ppp_listen;
+ msg.msg.ppp = pcb;
+ msg.msg.msg.listen.addrs = addrs;
+ TCPIP_PPPAPI(&msg);
+ return msg.msg.err;
+}
+#endif /* PPP_SERVER */
+
+
+/**
+ * Call ppp_close() inside the tcpip_thread context.
+ */
+static void
+pppapi_do_ppp_close(struct pppapi_msg_msg *msg)
+{
+ msg->err = ppp_close(msg->ppp, msg->msg.close.nocarrier);
+ TCPIP_PPPAPI_ACK(msg);
+}
+
+/**
+ * Call ppp_close() in a thread-safe way by running that function inside the
+ * tcpip_thread context.
+ */
+err_t
+pppapi_close(ppp_pcb *pcb, u8_t nocarrier)
+{
+ struct pppapi_msg msg;
+ msg.function = pppapi_do_ppp_close;
+ msg.msg.ppp = pcb;
+ msg.msg.msg.close.nocarrier = nocarrier;
+ TCPIP_PPPAPI(&msg);
+ return msg.msg.err;
+}
+
+
+/**
+ * Call ppp_free() inside the tcpip_thread context.
+ */
+static void
+pppapi_do_ppp_free(struct pppapi_msg_msg *msg)
+{
+ msg->err = ppp_free(msg->ppp);
+ TCPIP_PPPAPI_ACK(msg);
+}
+
+/**
+ * Call ppp_free() in a thread-safe way by running that function inside the
+ * tcpip_thread context.
+ */
+err_t
+pppapi_free(ppp_pcb *pcb)
+{
+ struct pppapi_msg msg;
+ msg.function = pppapi_do_ppp_free;
+ msg.msg.ppp = pcb;
+ TCPIP_PPPAPI(&msg);
+ return msg.msg.err;
+}
+
+
+/**
+ * Call ppp_ioctl() inside the tcpip_thread context.
+ */
+static void
+pppapi_do_ppp_ioctl(struct pppapi_msg_msg *msg)
+{
+ msg->err = ppp_ioctl(msg->ppp, msg->msg.ioctl.cmd, msg->msg.ioctl.arg);
+ TCPIP_PPPAPI_ACK(msg);
+}
+
+/**
+ * Call ppp_ioctl() in a thread-safe way by running that function inside the
+ * tcpip_thread context.
+ */
+err_t
+pppapi_ioctl(ppp_pcb *pcb, u8_t cmd, void *arg)
+{
+ struct pppapi_msg msg;
+ msg.function = pppapi_do_ppp_ioctl;
+ msg.msg.ppp = pcb;
+ msg.msg.msg.ioctl.cmd = cmd;
+ msg.msg.msg.ioctl.arg = arg;
+ TCPIP_PPPAPI(&msg);
+ return msg.msg.err;
+}
+
+
+#endif /* LWIP_PPP_API */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/api/sockets.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/api/sockets.c
new file mode 100644
index 0000000..b763248
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/api/sockets.c
@@ -0,0 +1,2827 @@
+/**
+ * @file
+ * Sockets BSD-Like API module
+ *
+ * @defgroup socket Socket API
+ * @ingroup sequential_api
+ * BSD-style socket API.\n
+ * Thread-safe, to be called from non-TCPIP threads only.\n
+ * Can be activated by defining @ref LWIP_SOCKET to 1.\n
+ * Header is in posix/sys/socket.h\b
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ * Improved by Marc Boucher <marc@mbsi.ca> and David Haas <dhaas@alum.rpi.edu>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/sockets.h"
+#include "lwip/api.h"
+#include "lwip/sys.h"
+#include "lwip/igmp.h"
+#include "lwip/inet.h"
+#include "lwip/tcp.h"
+#include "lwip/raw.h"
+#include "lwip/udp.h"
+#include "lwip/memp.h"
+#include "lwip/pbuf.h"
+#include "lwip/priv/tcpip_priv.h"
+#if LWIP_CHECKSUM_ON_COPY
+#include "lwip/inet_chksum.h"
+#endif
+
+#include <string.h>
+
+/* If the netconn API is not required publicly, then we include the necessary
+ files here to get the implementation */
+#if !LWIP_NETCONN
+#undef LWIP_NETCONN
+#define LWIP_NETCONN 1
+#include "api_msg.c"
+#include "api_lib.c"
+#include "netbuf.c"
+#undef LWIP_NETCONN
+#define LWIP_NETCONN 0
+#endif
+
+#if LWIP_IPV4
+#define IP4ADDR_PORT_TO_SOCKADDR(sin, ipaddr, port) do { \
+ (sin)->sin_len = sizeof(struct sockaddr_in); \
+ (sin)->sin_family = AF_INET; \
+ (sin)->sin_port = lwip_htons((port)); \
+ inet_addr_from_ip4addr(&(sin)->sin_addr, ipaddr); \
+ memset((sin)->sin_zero, 0, SIN_ZERO_LEN); }while(0)
+#define SOCKADDR4_TO_IP4ADDR_PORT(sin, ipaddr, port) do { \
+ inet_addr_to_ip4addr(ip_2_ip4(ipaddr), &((sin)->sin_addr)); \
+ (port) = lwip_ntohs((sin)->sin_port); }while(0)
+#endif /* LWIP_IPV4 */
+
+#if LWIP_IPV6
+#define IP6ADDR_PORT_TO_SOCKADDR(sin6, ipaddr, port) do { \
+ (sin6)->sin6_len = sizeof(struct sockaddr_in6); \
+ (sin6)->sin6_family = AF_INET6; \
+ (sin6)->sin6_port = lwip_htons((port)); \
+ (sin6)->sin6_flowinfo = 0; \
+ inet6_addr_from_ip6addr(&(sin6)->sin6_addr, ipaddr); \
+ (sin6)->sin6_scope_id = 0; }while(0)
+#define SOCKADDR6_TO_IP6ADDR_PORT(sin6, ipaddr, port) do { \
+ inet6_addr_to_ip6addr(ip_2_ip6(ipaddr), &((sin6)->sin6_addr)); \
+ (port) = lwip_ntohs((sin6)->sin6_port); }while(0)
+#endif /* LWIP_IPV6 */
+
+#if LWIP_IPV4 && LWIP_IPV6
+static void sockaddr_to_ipaddr_port(const struct sockaddr* sockaddr, ip_addr_t* ipaddr, u16_t* port);
+
+#define IS_SOCK_ADDR_LEN_VALID(namelen) (((namelen) == sizeof(struct sockaddr_in)) || \
+ ((namelen) == sizeof(struct sockaddr_in6)))
+#define IS_SOCK_ADDR_TYPE_VALID(name) (((name)->sa_family == AF_INET) || \
+ ((name)->sa_family == AF_INET6))
+#define SOCK_ADDR_TYPE_MATCH(name, sock) \
+ ((((name)->sa_family == AF_INET) && !(NETCONNTYPE_ISIPV6((sock)->conn->type))) || \
+ (((name)->sa_family == AF_INET6) && (NETCONNTYPE_ISIPV6((sock)->conn->type))))
+#define IPADDR_PORT_TO_SOCKADDR(sockaddr, ipaddr, port) do { \
+ if (IP_IS_V6(ipaddr)) { \
+ IP6ADDR_PORT_TO_SOCKADDR((struct sockaddr_in6*)(void*)(sockaddr), ip_2_ip6(ipaddr), port); \
+ } else { \
+ IP4ADDR_PORT_TO_SOCKADDR((struct sockaddr_in*)(void*)(sockaddr), ip_2_ip4(ipaddr), port); \
+ } } while(0)
+#define SOCKADDR_TO_IPADDR_PORT(sockaddr, ipaddr, port) sockaddr_to_ipaddr_port(sockaddr, ipaddr, &(port))
+#define DOMAIN_TO_NETCONN_TYPE(domain, type) (((domain) == AF_INET) ? \
+ (type) : (enum netconn_type)((type) | NETCONN_TYPE_IPV6))
+#elif LWIP_IPV6 /* LWIP_IPV4 && LWIP_IPV6 */
+#define IS_SOCK_ADDR_LEN_VALID(namelen) ((namelen) == sizeof(struct sockaddr_in6))
+#define IS_SOCK_ADDR_TYPE_VALID(name) ((name)->sa_family == AF_INET6)
+#define SOCK_ADDR_TYPE_MATCH(name, sock) 1
+#define IPADDR_PORT_TO_SOCKADDR(sockaddr, ipaddr, port) \
+ IP6ADDR_PORT_TO_SOCKADDR((struct sockaddr_in6*)(void*)(sockaddr), ip_2_ip6(ipaddr), port)
+#define SOCKADDR_TO_IPADDR_PORT(sockaddr, ipaddr, port) \
+ SOCKADDR6_TO_IP6ADDR_PORT((const struct sockaddr_in6*)(const void*)(sockaddr), ipaddr, port)
+#define DOMAIN_TO_NETCONN_TYPE(domain, netconn_type) (netconn_type)
+#else /*-> LWIP_IPV4: LWIP_IPV4 && LWIP_IPV6 */
+#define IS_SOCK_ADDR_LEN_VALID(namelen) ((namelen) == sizeof(struct sockaddr_in))
+#define IS_SOCK_ADDR_TYPE_VALID(name) ((name)->sa_family == AF_INET)
+#define SOCK_ADDR_TYPE_MATCH(name, sock) 1
+#define IPADDR_PORT_TO_SOCKADDR(sockaddr, ipaddr, port) \
+ IP4ADDR_PORT_TO_SOCKADDR((struct sockaddr_in*)(void*)(sockaddr), ip_2_ip4(ipaddr), port)
+#define SOCKADDR_TO_IPADDR_PORT(sockaddr, ipaddr, port) \
+ SOCKADDR4_TO_IP4ADDR_PORT((const struct sockaddr_in*)(const void*)(sockaddr), ipaddr, port)
+#define DOMAIN_TO_NETCONN_TYPE(domain, netconn_type) (netconn_type)
+#endif /* LWIP_IPV6 */
+
+#define IS_SOCK_ADDR_TYPE_VALID_OR_UNSPEC(name) (((name)->sa_family == AF_UNSPEC) || \
+ IS_SOCK_ADDR_TYPE_VALID(name))
+#define SOCK_ADDR_TYPE_MATCH_OR_UNSPEC(name, sock) (((name)->sa_family == AF_UNSPEC) || \
+ SOCK_ADDR_TYPE_MATCH(name, sock))
+#define IS_SOCK_ADDR_ALIGNED(name) ((((mem_ptr_t)(name)) % 4) == 0)
+
+
+#define LWIP_SOCKOPT_CHECK_OPTLEN(optlen, opttype) do { if ((optlen) < sizeof(opttype)) { return EINVAL; }}while(0)
+#define LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, optlen, opttype) do { \
+ LWIP_SOCKOPT_CHECK_OPTLEN(optlen, opttype); \
+ if ((sock)->conn == NULL) { return EINVAL; } }while(0)
+#define LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, opttype) do { \
+ LWIP_SOCKOPT_CHECK_OPTLEN(optlen, opttype); \
+ if (((sock)->conn == NULL) || ((sock)->conn->pcb.tcp == NULL)) { return EINVAL; } }while(0)
+#define LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, opttype, netconntype) do { \
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, opttype); \
+ if (NETCONNTYPE_GROUP(netconn_type((sock)->conn)) != netconntype) { return ENOPROTOOPT; } }while(0)
+
+
+#define LWIP_SETGETSOCKOPT_DATA_VAR_REF(name) API_VAR_REF(name)
+#define LWIP_SETGETSOCKOPT_DATA_VAR_DECLARE(name) API_VAR_DECLARE(struct lwip_setgetsockopt_data, name)
+#define LWIP_SETGETSOCKOPT_DATA_VAR_FREE(name) API_VAR_FREE(MEMP_SOCKET_SETGETSOCKOPT_DATA, name)
+#if LWIP_MPU_COMPATIBLE
+#define LWIP_SETGETSOCKOPT_DATA_VAR_ALLOC(name, sock) do { \
+ name = (struct lwip_setgetsockopt_data *)memp_malloc(MEMP_SOCKET_SETGETSOCKOPT_DATA); \
+ if (name == NULL) { \
+ sock_set_errno(sock, ENOMEM); \
+ return -1; \
+ } }while(0)
+#else /* LWIP_MPU_COMPATIBLE */
+#define LWIP_SETGETSOCKOPT_DATA_VAR_ALLOC(name, sock)
+#endif /* LWIP_MPU_COMPATIBLE */
+
+#if LWIP_SO_SNDRCVTIMEO_NONSTANDARD
+#define LWIP_SO_SNDRCVTIMEO_OPTTYPE int
+#define LWIP_SO_SNDRCVTIMEO_SET(optval, val) (*(int *)(optval) = (val))
+#define LWIP_SO_SNDRCVTIMEO_GET_MS(optval) ((s32_t)*(const int*)(optval))
+#else
+#define LWIP_SO_SNDRCVTIMEO_OPTTYPE struct timeval
+#define LWIP_SO_SNDRCVTIMEO_SET(optval, val) do { \
+ s32_t loc = (val); \
+ ((struct timeval *)(optval))->tv_sec = (loc) / 1000U; \
+ ((struct timeval *)(optval))->tv_usec = ((loc) % 1000U) * 1000U; }while(0)
+#define LWIP_SO_SNDRCVTIMEO_GET_MS(optval) ((((const struct timeval *)(optval))->tv_sec * 1000U) + (((const struct timeval *)(optval))->tv_usec / 1000U))
+#endif
+
+#define NUM_SOCKETS MEMP_NUM_NETCONN
+
+/** This is overridable for the rare case where more than 255 threads
+ * select on the same socket...
+ */
+#ifndef SELWAIT_T
+#define SELWAIT_T u8_t
+#endif
+
+/** Contains all internal pointers and states used for a socket */
+struct lwip_sock {
+ /** sockets currently are built on netconns, each socket has one netconn */
+ struct netconn *conn;
+ /** data that was left from the previous read */
+ void *lastdata;
+ /** offset in the data that was left from the previous read */
+ u16_t lastoffset;
+ /** number of times data was received, set by event_callback(),
+ tested by the receive and select functions */
+ s16_t rcvevent;
+ /** number of times data was ACKed (free send buffer), set by event_callback(),
+ tested by select */
+ u16_t sendevent;
+ /** error happened for this socket, set by event_callback(), tested by select */
+ u16_t errevent;
+ /** last error that occurred on this socket (in fact, all our errnos fit into an u8_t) */
+ u8_t err;
+ /** counter of how many threads are waiting for this socket using select */
+ SELWAIT_T select_waiting;
+};
+
+#if LWIP_NETCONN_SEM_PER_THREAD
+#define SELECT_SEM_T sys_sem_t*
+#define SELECT_SEM_PTR(sem) (sem)
+#else /* LWIP_NETCONN_SEM_PER_THREAD */
+#define SELECT_SEM_T sys_sem_t
+#define SELECT_SEM_PTR(sem) (&(sem))
+#endif /* LWIP_NETCONN_SEM_PER_THREAD */
+
+/** Description for a task waiting in select */
+struct lwip_select_cb {
+ /** Pointer to the next waiting task */
+ struct lwip_select_cb *next;
+ /** Pointer to the previous waiting task */
+ struct lwip_select_cb *prev;
+ /** readset passed to select */
+ fd_set *readset;
+ /** writeset passed to select */
+ fd_set *writeset;
+ /** unimplemented: exceptset passed to select */
+ fd_set *exceptset;
+ /** don't signal the same semaphore twice: set to 1 when signalled */
+ int sem_signalled;
+ /** semaphore to wake up a task waiting for select */
+ SELECT_SEM_T sem;
+};
+
+/** A struct sockaddr replacement that has the same alignment as sockaddr_in/
+ * sockaddr_in6 if instantiated.
+ */
+union sockaddr_aligned {
+ struct sockaddr sa;
+#if LWIP_IPV6
+ struct sockaddr_in6 sin6;
+#endif /* LWIP_IPV6 */
+#if LWIP_IPV4
+ struct sockaddr_in sin;
+#endif /* LWIP_IPV4 */
+};
+
+#if LWIP_IGMP
+/* Define the number of IPv4 multicast memberships, default is one per socket */
+#ifndef LWIP_SOCKET_MAX_MEMBERSHIPS
+#define LWIP_SOCKET_MAX_MEMBERSHIPS NUM_SOCKETS
+#endif
+
+/* This is to keep track of IP_ADD_MEMBERSHIP calls to drop the membership when
+ a socket is closed */
+struct lwip_socket_multicast_pair {
+ /** the socket */
+ struct lwip_sock* sock;
+ /** the interface address */
+ ip4_addr_t if_addr;
+ /** the group address */
+ ip4_addr_t multi_addr;
+};
+
+struct lwip_socket_multicast_pair socket_ipv4_multicast_memberships[LWIP_SOCKET_MAX_MEMBERSHIPS];
+
+static int lwip_socket_register_membership(int s, const ip4_addr_t *if_addr, const ip4_addr_t *multi_addr);
+static void lwip_socket_unregister_membership(int s, const ip4_addr_t *if_addr, const ip4_addr_t *multi_addr);
+static void lwip_socket_drop_registered_memberships(int s);
+#endif /* LWIP_IGMP */
+
+/** The global array of available sockets */
+static struct lwip_sock sockets[NUM_SOCKETS];
+/** The global list of tasks waiting for select */
+static struct lwip_select_cb *select_cb_list;
+/** This counter is increased from lwip_select when the list is changed
+ and checked in event_callback to see if it has changed. */
+static volatile int select_cb_ctr;
+
+#if LWIP_SOCKET_SET_ERRNO
+#ifndef set_errno
+#define set_errno(err) do { if (err) { errno = (err); } } while(0)
+#endif
+#else /* LWIP_SOCKET_SET_ERRNO */
+#define set_errno(err)
+#endif /* LWIP_SOCKET_SET_ERRNO */
+
+#define sock_set_errno(sk, e) do { \
+ const int sockerr = (e); \
+ sk->err = (u8_t)sockerr; \
+ set_errno(sockerr); \
+} while (0)
+
+/* Forward declaration of some functions */
+static void event_callback(struct netconn *conn, enum netconn_evt evt, u16_t len);
+#if !LWIP_TCPIP_CORE_LOCKING
+static void lwip_getsockopt_callback(void *arg);
+static void lwip_setsockopt_callback(void *arg);
+#endif
+static u8_t lwip_getsockopt_impl(int s, int level, int optname, void *optval, socklen_t *optlen);
+static u8_t lwip_setsockopt_impl(int s, int level, int optname, const void *optval, socklen_t optlen);
+
+#if LWIP_IPV4 && LWIP_IPV6
+static void
+sockaddr_to_ipaddr_port(const struct sockaddr* sockaddr, ip_addr_t* ipaddr, u16_t* port)
+{
+ if ((sockaddr->sa_family) == AF_INET6) {
+ SOCKADDR6_TO_IP6ADDR_PORT((const struct sockaddr_in6*)(const void*)(sockaddr), ipaddr, *port);
+ ipaddr->type = IPADDR_TYPE_V6;
+ } else {
+ SOCKADDR4_TO_IP4ADDR_PORT((const struct sockaddr_in*)(const void*)(sockaddr), ipaddr, *port);
+ ipaddr->type = IPADDR_TYPE_V4;
+ }
+}
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+
+/** LWIP_NETCONN_SEM_PER_THREAD==1: initialize thread-local semaphore */
+void
+lwip_socket_thread_init(void)
+{
+ netconn_thread_init();
+}
+
+/** LWIP_NETCONN_SEM_PER_THREAD==1: destroy thread-local semaphore */
+void
+lwip_socket_thread_cleanup(void)
+{
+ netconn_thread_cleanup();
+}
+
+/**
+ * Map a externally used socket index to the internal socket representation.
+ *
+ * @param s externally used socket index
+ * @return struct lwip_sock for the socket or NULL if not found
+ */
+static struct lwip_sock *
+get_socket(int s)
+{
+ struct lwip_sock *sock;
+
+ s -= LWIP_SOCKET_OFFSET;
+
+ if ((s < 0) || (s >= NUM_SOCKETS)) {
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("get_socket(%d): invalid\n", s + LWIP_SOCKET_OFFSET));
+ set_errno(EBADF);
+ return NULL;
+ }
+
+ sock = &sockets[s];
+
+ if (!sock->conn) {
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("get_socket(%d): not active\n", s + LWIP_SOCKET_OFFSET));
+ set_errno(EBADF);
+ return NULL;
+ }
+
+ return sock;
+}
+
+/**
+ * Same as get_socket but doesn't set errno
+ *
+ * @param s externally used socket index
+ * @return struct lwip_sock for the socket or NULL if not found
+ */
+static struct lwip_sock *
+tryget_socket(int s)
+{
+ s -= LWIP_SOCKET_OFFSET;
+ if ((s < 0) || (s >= NUM_SOCKETS)) {
+ return NULL;
+ }
+ if (!sockets[s].conn) {
+ return NULL;
+ }
+ return &sockets[s];
+}
+
+/**
+ * Allocate a new socket for a given netconn.
+ *
+ * @param newconn the netconn for which to allocate a socket
+ * @param accepted 1 if socket has been created by accept(),
+ * 0 if socket has been created by socket()
+ * @return the index of the new socket; -1 on error
+ */
+static int
+alloc_socket(struct netconn *newconn, int accepted)
+{
+ int i;
+ SYS_ARCH_DECL_PROTECT(lev);
+
+ /* allocate a new socket identifier */
+ for (i = 0; i < NUM_SOCKETS; ++i) {
+ /* Protect socket array */
+ SYS_ARCH_PROTECT(lev);
+ if (!sockets[i].conn && (sockets[i].select_waiting == 0)) {
+ sockets[i].conn = newconn;
+ /* The socket is not yet known to anyone, so no need to protect
+ after having marked it as used. */
+ SYS_ARCH_UNPROTECT(lev);
+ sockets[i].lastdata = NULL;
+ sockets[i].lastoffset = 0;
+ sockets[i].rcvevent = 0;
+ /* TCP sendbuf is empty, but the socket is not yet writable until connected
+ * (unless it has been created by accept()). */
+ sockets[i].sendevent = (NETCONNTYPE_GROUP(newconn->type) == NETCONN_TCP ? (accepted != 0) : 1);
+ sockets[i].errevent = 0;
+ sockets[i].err = 0;
+ return i + LWIP_SOCKET_OFFSET;
+ }
+ SYS_ARCH_UNPROTECT(lev);
+ }
+ return -1;
+}
+
+/** Free a socket. The socket's netconn must have been
+ * delete before!
+ *
+ * @param sock the socket to free
+ * @param is_tcp != 0 for TCP sockets, used to free lastdata
+ */
+static void
+free_socket(struct lwip_sock *sock, int is_tcp)
+{
+ void *lastdata;
+
+ lastdata = sock->lastdata;
+ sock->lastdata = NULL;
+ sock->lastoffset = 0;
+ sock->err = 0;
+
+ /* Protect socket array */
+ SYS_ARCH_SET(sock->conn, NULL);
+ /* don't use 'sock' after this line, as another task might have allocated it */
+
+ if (lastdata != NULL) {
+ if (is_tcp) {
+ pbuf_free((struct pbuf *)lastdata);
+ } else {
+ netbuf_delete((struct netbuf *)lastdata);
+ }
+ }
+}
+
+/* Below this, the well-known socket functions are implemented.
+ * Use google.com or opengroup.org to get a good description :-)
+ *
+ * Exceptions are documented!
+ */
+
+int
+lwip_accept(int s, struct sockaddr *addr, socklen_t *addrlen)
+{
+ struct lwip_sock *sock, *nsock;
+ struct netconn *newconn;
+ ip_addr_t naddr;
+ u16_t port = 0;
+ int newsock;
+ err_t err;
+ SYS_ARCH_DECL_PROTECT(lev);
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d)...\n", s));
+ sock = get_socket(s);
+ if (!sock) {
+ return -1;
+ }
+
+ if (netconn_is_nonblocking(sock->conn) && (sock->rcvevent <= 0)) {
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d): returning EWOULDBLOCK\n", s));
+ set_errno(EWOULDBLOCK);
+ return -1;
+ }
+
+ /* wait for a new connection */
+ err = netconn_accept(sock->conn, &newconn);
+ if (err != ERR_OK) {
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d): netconn_acept failed, err=%d\n", s, err));
+ if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) {
+ sock_set_errno(sock, EOPNOTSUPP);
+ } else if (err == ERR_CLSD) {
+ sock_set_errno(sock, EINVAL);
+ } else {
+ sock_set_errno(sock, err_to_errno(err));
+ }
+ return -1;
+ }
+ LWIP_ASSERT("newconn != NULL", newconn != NULL);
+
+ newsock = alloc_socket(newconn, 1);
+ if (newsock == -1) {
+ netconn_delete(newconn);
+ sock_set_errno(sock, ENFILE);
+ return -1;
+ }
+ LWIP_ASSERT("invalid socket index", (newsock >= LWIP_SOCKET_OFFSET) && (newsock < NUM_SOCKETS + LWIP_SOCKET_OFFSET));
+ LWIP_ASSERT("newconn->callback == event_callback", newconn->callback == event_callback);
+ nsock = &sockets[newsock - LWIP_SOCKET_OFFSET];
+
+ /* See event_callback: If data comes in right away after an accept, even
+ * though the server task might not have created a new socket yet.
+ * In that case, newconn->socket is counted down (newconn->socket--),
+ * so nsock->rcvevent is >= 1 here!
+ */
+ SYS_ARCH_PROTECT(lev);
+ nsock->rcvevent += (s16_t)(-1 - newconn->socket);
+ newconn->socket = newsock;
+ SYS_ARCH_UNPROTECT(lev);
+
+ /* Note that POSIX only requires us to check addr is non-NULL. addrlen must
+ * not be NULL if addr is valid.
+ */
+ if (addr != NULL) {
+ union sockaddr_aligned tempaddr;
+ /* get the IP address and port of the remote host */
+ err = netconn_peer(newconn, &naddr, &port);
+ if (err != ERR_OK) {
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d): netconn_peer failed, err=%d\n", s, err));
+ netconn_delete(newconn);
+ free_socket(nsock, 1);
+ sock_set_errno(sock, err_to_errno(err));
+ return -1;
+ }
+ LWIP_ASSERT("addr valid but addrlen NULL", addrlen != NULL);
+
+ IPADDR_PORT_TO_SOCKADDR(&tempaddr, &naddr, port);
+ if (*addrlen > tempaddr.sa.sa_len) {
+ *addrlen = tempaddr.sa.sa_len;
+ }
+ MEMCPY(addr, &tempaddr, *addrlen);
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d) returning new sock=%d addr=", s, newsock));
+ ip_addr_debug_print_val(SOCKETS_DEBUG, naddr);
+ LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F"\n", port));
+ } else {
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d) returning new sock=%d", s, newsock));
+ }
+
+ sock_set_errno(sock, 0);
+ return newsock;
+}
+
+int
+lwip_bind(int s, const struct sockaddr *name, socklen_t namelen)
+{
+ struct lwip_sock *sock;
+ ip_addr_t local_addr;
+ u16_t local_port;
+ err_t err;
+
+ sock = get_socket(s);
+ if (!sock) {
+ return -1;
+ }
+
+ if (!SOCK_ADDR_TYPE_MATCH(name, sock)) {
+ /* sockaddr does not match socket type (IPv4/IPv6) */
+ sock_set_errno(sock, err_to_errno(ERR_VAL));
+ return -1;
+ }
+
+ /* check size, family and alignment of 'name' */
+ LWIP_ERROR("lwip_bind: invalid address", (IS_SOCK_ADDR_LEN_VALID(namelen) &&
+ IS_SOCK_ADDR_TYPE_VALID(name) && IS_SOCK_ADDR_ALIGNED(name)),
+ sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;);
+ LWIP_UNUSED_ARG(namelen);
+
+ SOCKADDR_TO_IPADDR_PORT(name, &local_addr, local_port);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_bind(%d, addr=", s));
+ ip_addr_debug_print_val(SOCKETS_DEBUG, local_addr);
+ LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F")\n", local_port));
+
+#if LWIP_IPV4 && LWIP_IPV6
+ /* Dual-stack: Unmap IPv4 mapped IPv6 addresses */
+ if (IP_IS_V6_VAL(local_addr) && ip6_addr_isipv4mappedipv6(ip_2_ip6(&local_addr))) {
+ unmap_ipv4_mapped_ipv6(ip_2_ip4(&local_addr), ip_2_ip6(&local_addr));
+ IP_SET_TYPE_VAL(local_addr, IPADDR_TYPE_V4);
+ }
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+
+ err = netconn_bind(sock->conn, &local_addr, local_port);
+
+ if (err != ERR_OK) {
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_bind(%d) failed, err=%d\n", s, err));
+ sock_set_errno(sock, err_to_errno(err));
+ return -1;
+ }
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_bind(%d) succeeded\n", s));
+ sock_set_errno(sock, 0);
+ return 0;
+}
+
+int
+lwip_close(int s)
+{
+ struct lwip_sock *sock;
+ int is_tcp = 0;
+ err_t err;
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_close(%d)\n", s));
+
+ sock = get_socket(s);
+ if (!sock) {
+ return -1;
+ }
+
+ if (sock->conn != NULL) {
+ is_tcp = NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP;
+ } else {
+ LWIP_ASSERT("sock->lastdata == NULL", sock->lastdata == NULL);
+ }
+
+#if LWIP_IGMP
+ /* drop all possibly joined IGMP memberships */
+ lwip_socket_drop_registered_memberships(s);
+#endif /* LWIP_IGMP */
+
+ err = netconn_delete(sock->conn);
+ if (err != ERR_OK) {
+ sock_set_errno(sock, err_to_errno(err));
+ return -1;
+ }
+
+ free_socket(sock, is_tcp);
+ set_errno(0);
+ return 0;
+}
+
+int
+lwip_connect(int s, const struct sockaddr *name, socklen_t namelen)
+{
+ struct lwip_sock *sock;
+ err_t err;
+
+ sock = get_socket(s);
+ if (!sock) {
+ return -1;
+ }
+
+ if (!SOCK_ADDR_TYPE_MATCH_OR_UNSPEC(name, sock)) {
+ /* sockaddr does not match socket type (IPv4/IPv6) */
+ sock_set_errno(sock, err_to_errno(ERR_VAL));
+ return -1;
+ }
+
+ LWIP_UNUSED_ARG(namelen);
+ if (name->sa_family == AF_UNSPEC) {
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d, AF_UNSPEC)\n", s));
+ err = netconn_disconnect(sock->conn);
+ } else {
+ ip_addr_t remote_addr;
+ u16_t remote_port;
+
+ /* check size, family and alignment of 'name' */
+ LWIP_ERROR("lwip_connect: invalid address", IS_SOCK_ADDR_LEN_VALID(namelen) &&
+ IS_SOCK_ADDR_TYPE_VALID_OR_UNSPEC(name) && IS_SOCK_ADDR_ALIGNED(name),
+ sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;);
+
+ SOCKADDR_TO_IPADDR_PORT(name, &remote_addr, remote_port);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d, addr=", s));
+ ip_addr_debug_print_val(SOCKETS_DEBUG, remote_addr);
+ LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F")\n", remote_port));
+
+#if LWIP_IPV4 && LWIP_IPV6
+ /* Dual-stack: Unmap IPv4 mapped IPv6 addresses */
+ if (IP_IS_V6_VAL(remote_addr) && ip6_addr_isipv4mappedipv6(ip_2_ip6(&remote_addr))) {
+ unmap_ipv4_mapped_ipv6(ip_2_ip4(&remote_addr), ip_2_ip6(&remote_addr));
+ IP_SET_TYPE_VAL(remote_addr, IPADDR_TYPE_V4);
+ }
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+
+ err = netconn_connect(sock->conn, &remote_addr, remote_port);
+ }
+
+ if (err != ERR_OK) {
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d) failed, err=%d\n", s, err));
+ sock_set_errno(sock, err_to_errno(err));
+ return -1;
+ }
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d) succeeded\n", s));
+ sock_set_errno(sock, 0);
+ return 0;
+}
+
+/**
+ * Set a socket into listen mode.
+ * The socket may not have been used for another connection previously.
+ *
+ * @param s the socket to set to listening mode
+ * @param backlog (ATTENTION: needs TCP_LISTEN_BACKLOG=1)
+ * @return 0 on success, non-zero on failure
+ */
+int
+lwip_listen(int s, int backlog)
+{
+ struct lwip_sock *sock;
+ err_t err;
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_listen(%d, backlog=%d)\n", s, backlog));
+
+ sock = get_socket(s);
+ if (!sock) {
+ return -1;
+ }
+
+ /* limit the "backlog" parameter to fit in an u8_t */
+ backlog = LWIP_MIN(LWIP_MAX(backlog, 0), 0xff);
+
+ err = netconn_listen_with_backlog(sock->conn, (u8_t)backlog);
+
+ if (err != ERR_OK) {
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_listen(%d) failed, err=%d\n", s, err));
+ if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) {
+ sock_set_errno(sock, EOPNOTSUPP);
+ return -1;
+ }
+ sock_set_errno(sock, err_to_errno(err));
+ return -1;
+ }
+
+ sock_set_errno(sock, 0);
+ return 0;
+}
+
+int
+lwip_recvfrom(int s, void *mem, size_t len, int flags,
+ struct sockaddr *from, socklen_t *fromlen)
+{
+ struct lwip_sock *sock;
+ void *buf = NULL;
+ struct pbuf *p;
+ u16_t buflen, copylen;
+ int off = 0;
+ u8_t done = 0;
+ err_t err;
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d, %p, %"SZT_F", 0x%x, ..)\n", s, mem, len, flags));
+ sock = get_socket(s);
+ if (!sock) {
+ return -1;
+ }
+
+ do {
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: top while sock->lastdata=%p\n", sock->lastdata));
+ /* Check if there is data left from the last recv operation. */
+ if (sock->lastdata) {
+ buf = sock->lastdata;
+ } else {
+ /* If this is non-blocking call, then check first */
+ if (((flags & MSG_DONTWAIT) || netconn_is_nonblocking(sock->conn)) &&
+ (sock->rcvevent <= 0)) {
+ if (off > 0) {
+ /* already received data, return that */
+ sock_set_errno(sock, 0);
+ return off;
+ }
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d): returning EWOULDBLOCK\n", s));
+ set_errno(EWOULDBLOCK);
+ return -1;
+ }
+
+ /* No data was left from the previous operation, so we try to get
+ some from the network. */
+ if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) {
+ err = netconn_recv_tcp_pbuf(sock->conn, (struct pbuf **)&buf);
+ } else {
+ err = netconn_recv(sock->conn, (struct netbuf **)&buf);
+ }
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: netconn_recv err=%d, netbuf=%p\n",
+ err, buf));
+
+ if (err != ERR_OK) {
+ if (off > 0) {
+ if (err == ERR_CLSD) {
+ /* closed but already received data, ensure select gets the FIN, too */
+ event_callback(sock->conn, NETCONN_EVT_RCVPLUS, 0);
+ }
+ /* already received data, return that */
+ sock_set_errno(sock, 0);
+ return off;
+ }
+ /* We should really do some error checking here. */
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d): buf == NULL, error is \"%s\"!\n",
+ s, lwip_strerr(err)));
+ sock_set_errno(sock, err_to_errno(err));
+ if (err == ERR_CLSD) {
+ return 0;
+ } else {
+ return -1;
+ }
+ }
+ LWIP_ASSERT("buf != NULL", buf != NULL);
+ sock->lastdata = buf;
+ }
+
+ if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) {
+ p = (struct pbuf *)buf;
+ } else {
+ p = ((struct netbuf *)buf)->p;
+ }
+ buflen = p->tot_len;
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: buflen=%"U16_F" len=%"SZT_F" off=%d sock->lastoffset=%"U16_F"\n",
+ buflen, len, off, sock->lastoffset));
+
+ buflen -= sock->lastoffset;
+
+ if (len > buflen) {
+ copylen = buflen;
+ } else {
+ copylen = (u16_t)len;
+ }
+
+ /* copy the contents of the received buffer into
+ the supplied memory pointer mem */
+ pbuf_copy_partial(p, (u8_t*)mem + off, copylen, sock->lastoffset);
+
+ off += copylen;
+
+ if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) {
+ LWIP_ASSERT("invalid copylen, len would underflow", len >= copylen);
+ len -= copylen;
+ if ((len <= 0) ||
+ (p->flags & PBUF_FLAG_PUSH) ||
+ (sock->rcvevent <= 0) ||
+ ((flags & MSG_PEEK) != 0)) {
+ done = 1;
+ }
+ } else {
+ done = 1;
+ }
+
+ /* Check to see from where the data was.*/
+ if (done) {
+#if !SOCKETS_DEBUG
+ if (from && fromlen)
+#endif /* !SOCKETS_DEBUG */
+ {
+ u16_t port;
+ ip_addr_t tmpaddr;
+ ip_addr_t *fromaddr;
+ union sockaddr_aligned saddr;
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d): addr=", s));
+ if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) {
+ fromaddr = &tmpaddr;
+ netconn_getaddr(sock->conn, fromaddr, &port, 0);
+ } else {
+ port = netbuf_fromport((struct netbuf *)buf);
+ fromaddr = netbuf_fromaddr((struct netbuf *)buf);
+ }
+
+#if LWIP_IPV4 && LWIP_IPV6
+ /* Dual-stack: Map IPv4 addresses to IPv4 mapped IPv6 */
+ if (NETCONNTYPE_ISIPV6(netconn_type(sock->conn)) && IP_IS_V4(fromaddr)) {
+ ip4_2_ipv4_mapped_ipv6(ip_2_ip6(fromaddr), ip_2_ip4(fromaddr));
+ IP_SET_TYPE(fromaddr, IPADDR_TYPE_V6);
+ }
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+
+ IPADDR_PORT_TO_SOCKADDR(&saddr, fromaddr, port);
+ ip_addr_debug_print(SOCKETS_DEBUG, fromaddr);
+ LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F" len=%d\n", port, off));
+#if SOCKETS_DEBUG
+ if (from && fromlen)
+#endif /* SOCKETS_DEBUG */
+ {
+ if (*fromlen > saddr.sa.sa_len) {
+ *fromlen = saddr.sa.sa_len;
+ }
+ MEMCPY(from, &saddr, *fromlen);
+ }
+ }
+ }
+
+ /* If we don't peek the incoming message... */
+ if ((flags & MSG_PEEK) == 0) {
+ /* If this is a TCP socket, check if there is data left in the
+ buffer. If so, it should be saved in the sock structure for next
+ time around. */
+ if ((NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) && (buflen - copylen > 0)) {
+ sock->lastdata = buf;
+ sock->lastoffset += copylen;
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: lastdata now netbuf=%p\n", buf));
+ } else {
+ sock->lastdata = NULL;
+ sock->lastoffset = 0;
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: deleting netbuf=%p\n", buf));
+ if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) {
+ pbuf_free((struct pbuf *)buf);
+ } else {
+ netbuf_delete((struct netbuf *)buf);
+ }
+ buf = NULL;
+ }
+ }
+ } while (!done);
+
+ sock_set_errno(sock, 0);
+ return off;
+}
+
+int
+lwip_read(int s, void *mem, size_t len)
+{
+ return lwip_recvfrom(s, mem, len, 0, NULL, NULL);
+}
+
+int
+lwip_recv(int s, void *mem, size_t len, int flags)
+{
+ return lwip_recvfrom(s, mem, len, flags, NULL, NULL);
+}
+
+int
+lwip_send(int s, const void *data, size_t size, int flags)
+{
+ struct lwip_sock *sock;
+ err_t err;
+ u8_t write_flags;
+ size_t written;
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_send(%d, data=%p, size=%"SZT_F", flags=0x%x)\n",
+ s, data, size, flags));
+
+ sock = get_socket(s);
+ if (!sock) {
+ return -1;
+ }
+
+ if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) {
+#if (LWIP_UDP || LWIP_RAW)
+ return lwip_sendto(s, data, size, flags, NULL, 0);
+#else /* (LWIP_UDP || LWIP_RAW) */
+ sock_set_errno(sock, err_to_errno(ERR_ARG));
+ return -1;
+#endif /* (LWIP_UDP || LWIP_RAW) */
+ }
+
+ write_flags = NETCONN_COPY |
+ ((flags & MSG_MORE) ? NETCONN_MORE : 0) |
+ ((flags & MSG_DONTWAIT) ? NETCONN_DONTBLOCK : 0);
+ written = 0;
+ err = netconn_write_partly(sock->conn, data, size, write_flags, &written);
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_send(%d) err=%d written=%"SZT_F"\n", s, err, written));
+ sock_set_errno(sock, err_to_errno(err));
+ return (err == ERR_OK ? (int)written : -1);
+}
+
+int
+lwip_sendmsg(int s, const struct msghdr *msg, int flags)
+{
+ struct lwip_sock *sock;
+ int i;
+#if LWIP_TCP
+ u8_t write_flags;
+ size_t written;
+#endif
+ int size = 0;
+ err_t err = ERR_OK;
+
+ sock = get_socket(s);
+ if (!sock) {
+ return -1;
+ }
+
+ LWIP_ERROR("lwip_sendmsg: invalid msghdr", msg != NULL,
+ sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;);
+
+ LWIP_UNUSED_ARG(msg->msg_control);
+ LWIP_UNUSED_ARG(msg->msg_controllen);
+ LWIP_UNUSED_ARG(msg->msg_flags);
+ LWIP_ERROR("lwip_sendmsg: invalid msghdr iov", (msg->msg_iov != NULL && msg->msg_iovlen != 0),
+ sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;);
+
+ if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) {
+#if LWIP_TCP
+ write_flags = NETCONN_COPY |
+ ((flags & MSG_MORE) ? NETCONN_MORE : 0) |
+ ((flags & MSG_DONTWAIT) ? NETCONN_DONTBLOCK : 0);
+
+ for (i = 0; i < msg->msg_iovlen; i++) {
+ u8_t apiflags = write_flags;
+ if (i + 1 < msg->msg_iovlen) {
+ apiflags |= NETCONN_MORE;
+ }
+ written = 0;
+ err = netconn_write_partly(sock->conn, msg->msg_iov[i].iov_base, msg->msg_iov[i].iov_len, write_flags, &written);
+ if (err == ERR_OK) {
+ size += written;
+ /* check that the entire IO vector was accepected, if not return a partial write */
+ if (written != msg->msg_iov[i].iov_len)
+ break;
+ }
+ /* none of this IO vector was accepted, but previous was, return partial write and conceal ERR_WOULDBLOCK */
+ else if (err == ERR_WOULDBLOCK && size > 0) {
+ err = ERR_OK;
+ /* let ERR_WOULDBLOCK persist on the netconn since we are returning ERR_OK */
+ break;
+ } else {
+ size = -1;
+ break;
+ }
+ }
+ sock_set_errno(sock, err_to_errno(err));
+ return size;
+#else /* LWIP_TCP */
+ sock_set_errno(sock, err_to_errno(ERR_ARG));
+ return -1;
+#endif /* LWIP_TCP */
+ }
+ /* else, UDP and RAW NETCONNs */
+#if LWIP_UDP || LWIP_RAW
+ {
+ struct netbuf *chain_buf;
+
+ LWIP_UNUSED_ARG(flags);
+ LWIP_ERROR("lwip_sendmsg: invalid msghdr name", (((msg->msg_name == NULL) && (msg->msg_namelen == 0)) ||
+ IS_SOCK_ADDR_LEN_VALID(msg->msg_namelen)) ,
+ sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;);
+
+ /* initialize chain buffer with destination */
+ chain_buf = netbuf_new();
+ if (!chain_buf) {
+ sock_set_errno(sock, err_to_errno(ERR_MEM));
+ return -1;
+ }
+ if (msg->msg_name) {
+ u16_t remote_port;
+ SOCKADDR_TO_IPADDR_PORT((const struct sockaddr *)msg->msg_name, &chain_buf->addr, remote_port);
+ netbuf_fromport(chain_buf) = remote_port;
+ }
+#if LWIP_NETIF_TX_SINGLE_PBUF
+ for (i = 0; i < msg->msg_iovlen; i++) {
+ size += msg->msg_iov[i].iov_len;
+ }
+ /* Allocate a new netbuf and copy the data into it. */
+ if (netbuf_alloc(chain_buf, (u16_t)size) == NULL) {
+ err = ERR_MEM;
+ } else {
+ /* flatten the IO vectors */
+ size_t offset = 0;
+ for (i = 0; i < msg->msg_iovlen; i++) {
+ MEMCPY(&((u8_t*)chain_buf->p->payload)[offset], msg->msg_iov[i].iov_base, msg->msg_iov[i].iov_len);
+ offset += msg->msg_iov[i].iov_len;
+ }
+#if LWIP_CHECKSUM_ON_COPY
+ {
+ /* This can be improved by using LWIP_CHKSUM_COPY() and aggregating the checksum for each IO vector */
+ u16_t chksum = ~inet_chksum_pbuf(chain_buf->p);
+ netbuf_set_chksum(chain_buf, chksum);
+ }
+#endif /* LWIP_CHECKSUM_ON_COPY */
+ err = ERR_OK;
+ }
+#else /* LWIP_NETIF_TX_SINGLE_PBUF */
+ /* create a chained netbuf from the IO vectors. NOTE: we assemble a pbuf chain
+ manually to avoid having to allocate, chain, and delete a netbuf for each iov */
+ for (i = 0; i < msg->msg_iovlen; i++) {
+ struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, 0, PBUF_REF);
+ if (p == NULL) {
+ err = ERR_MEM; /* let netbuf_delete() cleanup chain_buf */
+ break;
+ }
+ p->payload = msg->msg_iov[i].iov_base;
+ LWIP_ASSERT("iov_len < u16_t", msg->msg_iov[i].iov_len <= 0xFFFF);
+ p->len = p->tot_len = (u16_t)msg->msg_iov[i].iov_len;
+ /* netbuf empty, add new pbuf */
+ if (chain_buf->p == NULL) {
+ chain_buf->p = chain_buf->ptr = p;
+ /* add pbuf to existing pbuf chain */
+ } else {
+ pbuf_cat(chain_buf->p, p);
+ }
+ }
+ /* save size of total chain */
+ if (err == ERR_OK) {
+ size = netbuf_len(chain_buf);
+ }
+#endif /* LWIP_NETIF_TX_SINGLE_PBUF */
+
+ if (err == ERR_OK) {
+#if LWIP_IPV4 && LWIP_IPV6
+ /* Dual-stack: Unmap IPv4 mapped IPv6 addresses */
+ if (IP_IS_V6_VAL(chain_buf->addr) && ip6_addr_isipv4mappedipv6(ip_2_ip6(&chain_buf->addr))) {
+ unmap_ipv4_mapped_ipv6(ip_2_ip4(&chain_buf->addr), ip_2_ip6(&chain_buf->addr));
+ IP_SET_TYPE_VAL(chain_buf->addr, IPADDR_TYPE_V4);
+ }
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+
+ /* send the data */
+ err = netconn_send(sock->conn, chain_buf);
+ }
+
+ /* deallocated the buffer */
+ netbuf_delete(chain_buf);
+
+ sock_set_errno(sock, err_to_errno(err));
+ return (err == ERR_OK ? size : -1);
+ }
+#else /* LWIP_UDP || LWIP_RAW */
+ sock_set_errno(sock, err_to_errno(ERR_ARG));
+ return -1;
+#endif /* LWIP_UDP || LWIP_RAW */
+}
+
+int
+lwip_sendto(int s, const void *data, size_t size, int flags,
+ const struct sockaddr *to, socklen_t tolen)
+{
+ struct lwip_sock *sock;
+ err_t err;
+ u16_t short_size;
+ u16_t remote_port;
+ struct netbuf buf;
+
+ sock = get_socket(s);
+ if (!sock) {
+ return -1;
+ }
+
+ if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) {
+#if LWIP_TCP
+ return lwip_send(s, data, size, flags);
+#else /* LWIP_TCP */
+ LWIP_UNUSED_ARG(flags);
+ sock_set_errno(sock, err_to_errno(ERR_ARG));
+ return -1;
+#endif /* LWIP_TCP */
+ }
+
+ /* @todo: split into multiple sendto's? */
+ LWIP_ASSERT("lwip_sendto: size must fit in u16_t", size <= 0xffff);
+ short_size = (u16_t)size;
+ LWIP_ERROR("lwip_sendto: invalid address", (((to == NULL) && (tolen == 0)) ||
+ (IS_SOCK_ADDR_LEN_VALID(tolen) &&
+ IS_SOCK_ADDR_TYPE_VALID(to) && IS_SOCK_ADDR_ALIGNED(to))),
+ sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;);
+ LWIP_UNUSED_ARG(tolen);
+
+ /* initialize a buffer */
+ buf.p = buf.ptr = NULL;
+#if LWIP_CHECKSUM_ON_COPY
+ buf.flags = 0;
+#endif /* LWIP_CHECKSUM_ON_COPY */
+ if (to) {
+ SOCKADDR_TO_IPADDR_PORT(to, &buf.addr, remote_port);
+ } else {
+ remote_port = 0;
+ ip_addr_set_any(NETCONNTYPE_ISIPV6(netconn_type(sock->conn)), &buf.addr);
+ }
+ netbuf_fromport(&buf) = remote_port;
+
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_sendto(%d, data=%p, short_size=%"U16_F", flags=0x%x to=",
+ s, data, short_size, flags));
+ ip_addr_debug_print(SOCKETS_DEBUG, &buf.addr);
+ LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F"\n", remote_port));
+
+ /* make the buffer point to the data that should be sent */
+#if LWIP_NETIF_TX_SINGLE_PBUF
+ /* Allocate a new netbuf and copy the data into it. */
+ if (netbuf_alloc(&buf, short_size) == NULL) {
+ err = ERR_MEM;
+ } else {
+#if LWIP_CHECKSUM_ON_COPY
+ if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_RAW) {
+ u16_t chksum = LWIP_CHKSUM_COPY(buf.p->payload, data, short_size);
+ netbuf_set_chksum(&buf, chksum);
+ } else
+#endif /* LWIP_CHECKSUM_ON_COPY */
+ {
+ MEMCPY(buf.p->payload, data, short_size);
+ }
+ err = ERR_OK;
+ }
+#else /* LWIP_NETIF_TX_SINGLE_PBUF */
+ err = netbuf_ref(&buf, data, short_size);
+#endif /* LWIP_NETIF_TX_SINGLE_PBUF */
+ if (err == ERR_OK) {
+#if LWIP_IPV4 && LWIP_IPV6
+ /* Dual-stack: Unmap IPv4 mapped IPv6 addresses */
+ if (IP_IS_V6_VAL(buf.addr) && ip6_addr_isipv4mappedipv6(ip_2_ip6(&buf.addr))) {
+ unmap_ipv4_mapped_ipv6(ip_2_ip4(&buf.addr), ip_2_ip6(&buf.addr));
+ IP_SET_TYPE_VAL(buf.addr, IPADDR_TYPE_V4);
+ }
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+
+ /* send the data */
+ err = netconn_send(sock->conn, &buf);
+ }
+
+ /* deallocated the buffer */
+ netbuf_free(&buf);
+
+ sock_set_errno(sock, err_to_errno(err));
+ return (err == ERR_OK ? short_size : -1);
+}
+
+int
+lwip_socket(int domain, int type, int protocol)
+{
+ struct netconn *conn;
+ int i;
+
+ LWIP_UNUSED_ARG(domain); /* @todo: check this */
+
+ /* create a netconn */
+ switch (type) {
+ case SOCK_RAW:
+ conn = netconn_new_with_proto_and_callback(DOMAIN_TO_NETCONN_TYPE(domain, NETCONN_RAW),
+ (u8_t)protocol, event_callback);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%s, SOCK_RAW, %d) = ",
+ domain == PF_INET ? "PF_INET" : "UNKNOWN", protocol));
+ break;
+ case SOCK_DGRAM:
+ conn = netconn_new_with_callback(DOMAIN_TO_NETCONN_TYPE(domain,
+ ((protocol == IPPROTO_UDPLITE) ? NETCONN_UDPLITE : NETCONN_UDP)) ,
+ event_callback);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%s, SOCK_DGRAM, %d) = ",
+ domain == PF_INET ? "PF_INET" : "UNKNOWN", protocol));
+ break;
+ case SOCK_STREAM:
+ conn = netconn_new_with_callback(DOMAIN_TO_NETCONN_TYPE(domain, NETCONN_TCP), event_callback);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%s, SOCK_STREAM, %d) = ",
+ domain == PF_INET ? "PF_INET" : "UNKNOWN", protocol));
+ break;
+ default:
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%d, %d/UNKNOWN, %d) = -1\n",
+ domain, type, protocol));
+ set_errno(EINVAL);
+ return -1;
+ }
+
+ if (!conn) {
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("-1 / ENOBUFS (could not create netconn)\n"));
+ set_errno(ENOBUFS);
+ return -1;
+ }
+
+ i = alloc_socket(conn, 0);
+
+ if (i == -1) {
+ netconn_delete(conn);
+ set_errno(ENFILE);
+ return -1;
+ }
+ conn->socket = i;
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("%d\n", i));
+ set_errno(0);
+ return i;
+}
+
+int
+lwip_write(int s, const void *data, size_t size)
+{
+ return lwip_send(s, data, size, 0);
+}
+
+int
+lwip_writev(int s, const struct iovec *iov, int iovcnt)
+{
+ struct msghdr msg;
+
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ /* Hack: we have to cast via number to cast from 'const' pointer to non-const.
+ Blame the opengroup standard for this inconsistency. */
+ msg.msg_iov = LWIP_CONST_CAST(struct iovec *, iov);
+ msg.msg_iovlen = iovcnt;
+ msg.msg_control = NULL;
+ msg.msg_controllen = 0;
+ msg.msg_flags = 0;
+ return lwip_sendmsg(s, &msg, 0);
+}
+
+/**
+ * Go through the readset and writeset lists and see which socket of the sockets
+ * set in the sets has events. On return, readset, writeset and exceptset have
+ * the sockets enabled that had events.
+ *
+ * @param maxfdp1 the highest socket index in the sets
+ * @param readset_in set of sockets to check for read events
+ * @param writeset_in set of sockets to check for write events
+ * @param exceptset_in set of sockets to check for error events
+ * @param readset_out set of sockets that had read events
+ * @param writeset_out set of sockets that had write events
+ * @param exceptset_out set os sockets that had error events
+ * @return number of sockets that had events (read/write/exception) (>= 0)
+ */
+static int
+lwip_selscan(int maxfdp1, fd_set *readset_in, fd_set *writeset_in, fd_set *exceptset_in,
+ fd_set *readset_out, fd_set *writeset_out, fd_set *exceptset_out)
+{
+ int i, nready = 0;
+ fd_set lreadset, lwriteset, lexceptset;
+ struct lwip_sock *sock;
+ SYS_ARCH_DECL_PROTECT(lev);
+
+ FD_ZERO(&lreadset);
+ FD_ZERO(&lwriteset);
+ FD_ZERO(&lexceptset);
+
+ /* Go through each socket in each list to count number of sockets which
+ currently match */
+ for (i = LWIP_SOCKET_OFFSET; i < maxfdp1; i++) {
+ /* if this FD is not in the set, continue */
+ if (!(readset_in && FD_ISSET(i, readset_in)) &&
+ !(writeset_in && FD_ISSET(i, writeset_in)) &&
+ !(exceptset_in && FD_ISSET(i, exceptset_in))) {
+ continue;
+ }
+ /* First get the socket's status (protected)... */
+ SYS_ARCH_PROTECT(lev);
+ sock = tryget_socket(i);
+ if (sock != NULL) {
+ void* lastdata = sock->lastdata;
+ s16_t rcvevent = sock->rcvevent;
+ u16_t sendevent = sock->sendevent;
+ u16_t errevent = sock->errevent;
+ SYS_ARCH_UNPROTECT(lev);
+
+ /* ... then examine it: */
+ /* See if netconn of this socket is ready for read */
+ if (readset_in && FD_ISSET(i, readset_in) && ((lastdata != NULL) || (rcvevent > 0))) {
+ FD_SET(i, &lreadset);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_selscan: fd=%d ready for reading\n", i));
+ nready++;
+ }
+ /* See if netconn of this socket is ready for write */
+ if (writeset_in && FD_ISSET(i, writeset_in) && (sendevent != 0)) {
+ FD_SET(i, &lwriteset);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_selscan: fd=%d ready for writing\n", i));
+ nready++;
+ }
+ /* See if netconn of this socket had an error */
+ if (exceptset_in && FD_ISSET(i, exceptset_in) && (errevent != 0)) {
+ FD_SET(i, &lexceptset);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_selscan: fd=%d ready for exception\n", i));
+ nready++;
+ }
+ } else {
+ SYS_ARCH_UNPROTECT(lev);
+ /* continue on to next FD in list */
+ }
+ }
+ /* copy local sets to the ones provided as arguments */
+ *readset_out = lreadset;
+ *writeset_out = lwriteset;
+ *exceptset_out = lexceptset;
+
+ LWIP_ASSERT("nready >= 0", nready >= 0);
+ return nready;
+}
+
+int
+lwip_select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset,
+ struct timeval *timeout)
+{
+ u32_t waitres = 0;
+ int nready;
+ fd_set lreadset, lwriteset, lexceptset;
+ u32_t msectimeout;
+ struct lwip_select_cb select_cb;
+ int i;
+ int maxfdp2;
+#if LWIP_NETCONN_SEM_PER_THREAD
+ int waited = 0;
+#endif
+ SYS_ARCH_DECL_PROTECT(lev);
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select(%d, %p, %p, %p, tvsec=%"S32_F" tvusec=%"S32_F")\n",
+ maxfdp1, (void *)readset, (void *) writeset, (void *) exceptset,
+ timeout ? (s32_t)timeout->tv_sec : (s32_t)-1,
+ timeout ? (s32_t)timeout->tv_usec : (s32_t)-1));
+
+ /* Go through each socket in each list to count number of sockets which
+ currently match */
+ nready = lwip_selscan(maxfdp1, readset, writeset, exceptset, &lreadset, &lwriteset, &lexceptset);
+
+ /* If we don't have any current events, then suspend if we are supposed to */
+ if (!nready) {
+ if (timeout && timeout->tv_sec == 0 && timeout->tv_usec == 0) {
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: no timeout, returning 0\n"));
+ /* This is OK as the local fdsets are empty and nready is zero,
+ or we would have returned earlier. */
+ goto return_copy_fdsets;
+ }
+
+ /* None ready: add our semaphore to list:
+ We don't actually need any dynamic memory. Our entry on the
+ list is only valid while we are in this function, so it's ok
+ to use local variables. */
+
+ select_cb.next = NULL;
+ select_cb.prev = NULL;
+ select_cb.readset = readset;
+ select_cb.writeset = writeset;
+ select_cb.exceptset = exceptset;
+ select_cb.sem_signalled = 0;
+#if LWIP_NETCONN_SEM_PER_THREAD
+ select_cb.sem = LWIP_NETCONN_THREAD_SEM_GET();
+#else /* LWIP_NETCONN_SEM_PER_THREAD */
+ if (sys_sem_new(&select_cb.sem, 0) != ERR_OK) {
+ /* failed to create semaphore */
+ set_errno(ENOMEM);
+ return -1;
+ }
+#endif /* LWIP_NETCONN_SEM_PER_THREAD */
+
+ /* Protect the select_cb_list */
+ SYS_ARCH_PROTECT(lev);
+
+ /* Put this select_cb on top of list */
+ select_cb.next = select_cb_list;
+ if (select_cb_list != NULL) {
+ select_cb_list->prev = &select_cb;
+ }
+ select_cb_list = &select_cb;
+ /* Increasing this counter tells event_callback that the list has changed. */
+ select_cb_ctr++;
+
+ /* Now we can safely unprotect */
+ SYS_ARCH_UNPROTECT(lev);
+
+ /* Increase select_waiting for each socket we are interested in */
+ maxfdp2 = maxfdp1;
+ for (i = LWIP_SOCKET_OFFSET; i < maxfdp1; i++) {
+ if ((readset && FD_ISSET(i, readset)) ||
+ (writeset && FD_ISSET(i, writeset)) ||
+ (exceptset && FD_ISSET(i, exceptset))) {
+ struct lwip_sock *sock;
+ SYS_ARCH_PROTECT(lev);
+ sock = tryget_socket(i);
+ if (sock != NULL) {
+ sock->select_waiting++;
+ LWIP_ASSERT("sock->select_waiting > 0", sock->select_waiting > 0);
+ } else {
+ /* Not a valid socket */
+ nready = -1;
+ maxfdp2 = i;
+ SYS_ARCH_UNPROTECT(lev);
+ break;
+ }
+ SYS_ARCH_UNPROTECT(lev);
+ }
+ }
+
+ if (nready >= 0) {
+ /* Call lwip_selscan again: there could have been events between
+ the last scan (without us on the list) and putting us on the list! */
+ nready = lwip_selscan(maxfdp1, readset, writeset, exceptset, &lreadset, &lwriteset, &lexceptset);
+ if (!nready) {
+ /* Still none ready, just wait to be woken */
+ if (timeout == 0) {
+ /* Wait forever */
+ msectimeout = 0;
+ } else {
+ msectimeout = ((timeout->tv_sec * 1000) + ((timeout->tv_usec + 500)/1000));
+ if (msectimeout == 0) {
+ /* Wait 1ms at least (0 means wait forever) */
+ msectimeout = 1;
+ }
+ }
+
+ waitres = sys_arch_sem_wait(SELECT_SEM_PTR(select_cb.sem), msectimeout);
+#if LWIP_NETCONN_SEM_PER_THREAD
+ waited = 1;
+#endif
+ }
+ }
+
+ /* Decrease select_waiting for each socket we are interested in */
+ for (i = LWIP_SOCKET_OFFSET; i < maxfdp2; i++) {
+ if ((readset && FD_ISSET(i, readset)) ||
+ (writeset && FD_ISSET(i, writeset)) ||
+ (exceptset && FD_ISSET(i, exceptset))) {
+ struct lwip_sock *sock;
+ SYS_ARCH_PROTECT(lev);
+ sock = tryget_socket(i);
+ if (sock != NULL) {
+ /* for now, handle select_waiting==0... */
+ LWIP_ASSERT("sock->select_waiting > 0", sock->select_waiting > 0);
+ if (sock->select_waiting > 0) {
+ sock->select_waiting--;
+ }
+ } else {
+ /* Not a valid socket */
+ nready = -1;
+ }
+ SYS_ARCH_UNPROTECT(lev);
+ }
+ }
+ /* Take us off the list */
+ SYS_ARCH_PROTECT(lev);
+ if (select_cb.next != NULL) {
+ select_cb.next->prev = select_cb.prev;
+ }
+ if (select_cb_list == &select_cb) {
+ LWIP_ASSERT("select_cb.prev == NULL", select_cb.prev == NULL);
+ select_cb_list = select_cb.next;
+ } else {
+ LWIP_ASSERT("select_cb.prev != NULL", select_cb.prev != NULL);
+ select_cb.prev->next = select_cb.next;
+ }
+ /* Increasing this counter tells event_callback that the list has changed. */
+ select_cb_ctr++;
+ SYS_ARCH_UNPROTECT(lev);
+
+#if LWIP_NETCONN_SEM_PER_THREAD
+ if (select_cb.sem_signalled && (!waited || (waitres == SYS_ARCH_TIMEOUT))) {
+ /* don't leave the thread-local semaphore signalled */
+ sys_arch_sem_wait(select_cb.sem, 1);
+ }
+#else /* LWIP_NETCONN_SEM_PER_THREAD */
+ sys_sem_free(&select_cb.sem);
+#endif /* LWIP_NETCONN_SEM_PER_THREAD */
+
+ if (nready < 0) {
+ /* This happens when a socket got closed while waiting */
+ set_errno(EBADF);
+ return -1;
+ }
+
+ if (waitres == SYS_ARCH_TIMEOUT) {
+ /* Timeout */
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: timeout expired\n"));
+ /* This is OK as the local fdsets are empty and nready is zero,
+ or we would have returned earlier. */
+ goto return_copy_fdsets;
+ }
+
+ /* See what's set */
+ nready = lwip_selscan(maxfdp1, readset, writeset, exceptset, &lreadset, &lwriteset, &lexceptset);
+ }
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: nready=%d\n", nready));
+return_copy_fdsets:
+ set_errno(0);
+ if (readset) {
+ *readset = lreadset;
+ }
+ if (writeset) {
+ *writeset = lwriteset;
+ }
+ if (exceptset) {
+ *exceptset = lexceptset;
+ }
+ return nready;
+}
+
+/**
+ * Callback registered in the netconn layer for each socket-netconn.
+ * Processes recvevent (data available) and wakes up tasks waiting for select.
+ */
+static void
+event_callback(struct netconn *conn, enum netconn_evt evt, u16_t len)
+{
+ int s;
+ struct lwip_sock *sock;
+ struct lwip_select_cb *scb;
+ int last_select_cb_ctr;
+ SYS_ARCH_DECL_PROTECT(lev);
+
+ LWIP_UNUSED_ARG(len);
+
+ /* Get socket */
+ if (conn) {
+ s = conn->socket;
+ if (s < 0) {
+ /* Data comes in right away after an accept, even though
+ * the server task might not have created a new socket yet.
+ * Just count down (or up) if that's the case and we
+ * will use the data later. Note that only receive events
+ * can happen before the new socket is set up. */
+ SYS_ARCH_PROTECT(lev);
+ if (conn->socket < 0) {
+ if (evt == NETCONN_EVT_RCVPLUS) {
+ conn->socket--;
+ }
+ SYS_ARCH_UNPROTECT(lev);
+ return;
+ }
+ s = conn->socket;
+ SYS_ARCH_UNPROTECT(lev);
+ }
+
+ sock = get_socket(s);
+ if (!sock) {
+ return;
+ }
+ } else {
+ return;
+ }
+
+ SYS_ARCH_PROTECT(lev);
+ /* Set event as required */
+ switch (evt) {
+ case NETCONN_EVT_RCVPLUS:
+ sock->rcvevent++;
+ break;
+ case NETCONN_EVT_RCVMINUS:
+ sock->rcvevent--;
+ break;
+ case NETCONN_EVT_SENDPLUS:
+ sock->sendevent = 1;
+ break;
+ case NETCONN_EVT_SENDMINUS:
+ sock->sendevent = 0;
+ break;
+ case NETCONN_EVT_ERROR:
+ sock->errevent = 1;
+ break;
+ default:
+ LWIP_ASSERT("unknown event", 0);
+ break;
+ }
+
+ if (sock->select_waiting == 0) {
+ /* noone is waiting for this socket, no need to check select_cb_list */
+ SYS_ARCH_UNPROTECT(lev);
+ return;
+ }
+
+ /* Now decide if anyone is waiting for this socket */
+ /* NOTE: This code goes through the select_cb_list list multiple times
+ ONLY IF a select was actually waiting. We go through the list the number
+ of waiting select calls + 1. This list is expected to be small. */
+
+ /* At this point, SYS_ARCH is still protected! */
+again:
+ for (scb = select_cb_list; scb != NULL; scb = scb->next) {
+ /* remember the state of select_cb_list to detect changes */
+ last_select_cb_ctr = select_cb_ctr;
+ if (scb->sem_signalled == 0) {
+ /* semaphore not signalled yet */
+ int do_signal = 0;
+ /* Test this select call for our socket */
+ if (sock->rcvevent > 0) {
+ if (scb->readset && FD_ISSET(s, scb->readset)) {
+ do_signal = 1;
+ }
+ }
+ if (sock->sendevent != 0) {
+ if (!do_signal && scb->writeset && FD_ISSET(s, scb->writeset)) {
+ do_signal = 1;
+ }
+ }
+ if (sock->errevent != 0) {
+ if (!do_signal && scb->exceptset && FD_ISSET(s, scb->exceptset)) {
+ do_signal = 1;
+ }
+ }
+ if (do_signal) {
+ scb->sem_signalled = 1;
+ /* Don't call SYS_ARCH_UNPROTECT() before signaling the semaphore, as this might
+ lead to the select thread taking itself off the list, invalidating the semaphore. */
+ sys_sem_signal(SELECT_SEM_PTR(scb->sem));
+ }
+ }
+ /* unlock interrupts with each step */
+ SYS_ARCH_UNPROTECT(lev);
+ /* this makes sure interrupt protection time is short */
+ SYS_ARCH_PROTECT(lev);
+ if (last_select_cb_ctr != select_cb_ctr) {
+ /* someone has changed select_cb_list, restart at the beginning */
+ goto again;
+ }
+ }
+ SYS_ARCH_UNPROTECT(lev);
+}
+
+/**
+ * Close one end of a full-duplex connection.
+ */
+int
+lwip_shutdown(int s, int how)
+{
+ struct lwip_sock *sock;
+ err_t err;
+ u8_t shut_rx = 0, shut_tx = 0;
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_shutdown(%d, how=%d)\n", s, how));
+
+ sock = get_socket(s);
+ if (!sock) {
+ return -1;
+ }
+
+ if (sock->conn != NULL) {
+ if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) {
+ sock_set_errno(sock, EOPNOTSUPP);
+ return -1;
+ }
+ } else {
+ sock_set_errno(sock, ENOTCONN);
+ return -1;
+ }
+
+ if (how == SHUT_RD) {
+ shut_rx = 1;
+ } else if (how == SHUT_WR) {
+ shut_tx = 1;
+ } else if (how == SHUT_RDWR) {
+ shut_rx = 1;
+ shut_tx = 1;
+ } else {
+ sock_set_errno(sock, EINVAL);
+ return -1;
+ }
+ err = netconn_shutdown(sock->conn, shut_rx, shut_tx);
+
+ sock_set_errno(sock, err_to_errno(err));
+ return (err == ERR_OK ? 0 : -1);
+}
+
+static int
+lwip_getaddrname(int s, struct sockaddr *name, socklen_t *namelen, u8_t local)
+{
+ struct lwip_sock *sock;
+ union sockaddr_aligned saddr;
+ ip_addr_t naddr;
+ u16_t port;
+ err_t err;
+
+ sock = get_socket(s);
+ if (!sock) {
+ return -1;
+ }
+
+ /* get the IP address and port */
+ err = netconn_getaddr(sock->conn, &naddr, &port, local);
+ if (err != ERR_OK) {
+ sock_set_errno(sock, err_to_errno(err));
+ return -1;
+ }
+
+#if LWIP_IPV4 && LWIP_IPV6
+ /* Dual-stack: Map IPv4 addresses to IPv4 mapped IPv6 */
+ if (NETCONNTYPE_ISIPV6(netconn_type(sock->conn)) &&
+ IP_IS_V4_VAL(naddr)) {
+ ip4_2_ipv4_mapped_ipv6(ip_2_ip6(&naddr), ip_2_ip4(&naddr));
+ IP_SET_TYPE_VAL(naddr, IPADDR_TYPE_V6);
+ }
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+
+ IPADDR_PORT_TO_SOCKADDR(&saddr, &naddr, port);
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getaddrname(%d, addr=", s));
+ ip_addr_debug_print_val(SOCKETS_DEBUG, naddr);
+ LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F")\n", port));
+
+ if (*namelen > saddr.sa.sa_len) {
+ *namelen = saddr.sa.sa_len;
+ }
+ MEMCPY(name, &saddr, *namelen);
+
+ sock_set_errno(sock, 0);
+ return 0;
+}
+
+int
+lwip_getpeername(int s, struct sockaddr *name, socklen_t *namelen)
+{
+ return lwip_getaddrname(s, name, namelen, 0);
+}
+
+int
+lwip_getsockname(int s, struct sockaddr *name, socklen_t *namelen)
+{
+ return lwip_getaddrname(s, name, namelen, 1);
+}
+
+int
+lwip_getsockopt(int s, int level, int optname, void *optval, socklen_t *optlen)
+{
+ u8_t err;
+ struct lwip_sock *sock = get_socket(s);
+#if !LWIP_TCPIP_CORE_LOCKING
+ LWIP_SETGETSOCKOPT_DATA_VAR_DECLARE(data);
+#endif /* !LWIP_TCPIP_CORE_LOCKING */
+
+ if (!sock) {
+ return -1;
+ }
+
+ if ((NULL == optval) || (NULL == optlen)) {
+ sock_set_errno(sock, EFAULT);
+ return -1;
+ }
+
+#if LWIP_TCPIP_CORE_LOCKING
+ /* core-locking can just call the -impl function */
+ LOCK_TCPIP_CORE();
+ err = lwip_getsockopt_impl(s, level, optname, optval, optlen);
+ UNLOCK_TCPIP_CORE();
+
+#else /* LWIP_TCPIP_CORE_LOCKING */
+
+#if LWIP_MPU_COMPATIBLE
+ /* MPU_COMPATIBLE copies the optval data, so check for max size here */
+ if (*optlen > LWIP_SETGETSOCKOPT_MAXOPTLEN) {
+ sock_set_errno(sock, ENOBUFS);
+ return -1;
+ }
+#endif /* LWIP_MPU_COMPATIBLE */
+
+ LWIP_SETGETSOCKOPT_DATA_VAR_ALLOC(data, sock);
+ LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).s = s;
+ LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).level = level;
+ LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optname = optname;
+ LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optlen = *optlen;
+#if !LWIP_MPU_COMPATIBLE
+ LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optval.p = optval;
+#endif /* !LWIP_MPU_COMPATIBLE */
+ LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).err = 0;
+#if LWIP_NETCONN_SEM_PER_THREAD
+ LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem = LWIP_NETCONN_THREAD_SEM_GET();
+#else
+ LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem = &sock->conn->op_completed;
+#endif
+ err = tcpip_callback(lwip_getsockopt_callback, &LWIP_SETGETSOCKOPT_DATA_VAR_REF(data));
+ if (err != ERR_OK) {
+ LWIP_SETGETSOCKOPT_DATA_VAR_FREE(data);
+ sock_set_errno(sock, err_to_errno(err));
+ return -1;
+ }
+ sys_arch_sem_wait((sys_sem_t*)(LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem), 0);
+
+ /* write back optlen and optval */
+ *optlen = LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optlen;
+#if LWIP_MPU_COMPATIBLE
+ MEMCPY(optval, LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optval,
+ LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optlen);
+#endif /* LWIP_MPU_COMPATIBLE */
+
+ /* maybe lwip_getsockopt_internal has changed err */
+ err = LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).err;
+ LWIP_SETGETSOCKOPT_DATA_VAR_FREE(data);
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+
+ sock_set_errno(sock, err);
+ return err ? -1 : 0;
+}
+
+#if !LWIP_TCPIP_CORE_LOCKING
+/** lwip_getsockopt_callback: only used without CORE_LOCKING
+ * to get into the tcpip_thread
+ */
+static void
+lwip_getsockopt_callback(void *arg)
+{
+ struct lwip_setgetsockopt_data *data;
+ LWIP_ASSERT("arg != NULL", arg != NULL);
+ data = (struct lwip_setgetsockopt_data*)arg;
+
+ data->err = lwip_getsockopt_impl(data->s, data->level, data->optname,
+#if LWIP_MPU_COMPATIBLE
+ data->optval,
+#else /* LWIP_MPU_COMPATIBLE */
+ data->optval.p,
+#endif /* LWIP_MPU_COMPATIBLE */
+ &data->optlen);
+
+ sys_sem_signal((sys_sem_t*)(data->completed_sem));
+}
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+
+/** lwip_getsockopt_impl: the actual implementation of getsockopt:
+ * same argument as lwip_getsockopt, either called directly or through callback
+ */
+static u8_t
+lwip_getsockopt_impl(int s, int level, int optname, void *optval, socklen_t *optlen)
+{
+ u8_t err = 0;
+ struct lwip_sock *sock = tryget_socket(s);
+ if (!sock) {
+ return EBADF;
+ }
+
+ switch (level) {
+
+/* Level: SOL_SOCKET */
+ case SOL_SOCKET:
+ switch (optname) {
+
+#if LWIP_TCP
+ case SO_ACCEPTCONN:
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, int);
+ if (NETCONNTYPE_GROUP(sock->conn->type) != NETCONN_TCP) {
+ return ENOPROTOOPT;
+ }
+ if ((sock->conn->pcb.tcp != NULL) && (sock->conn->pcb.tcp->state == LISTEN)) {
+ *(int*)optval = 1;
+ } else {
+ *(int*)optval = 0;
+ }
+ break;
+#endif /* LWIP_TCP */
+
+ /* The option flags */
+ case SO_BROADCAST:
+ case SO_KEEPALIVE:
+#if SO_REUSE
+ case SO_REUSEADDR:
+#endif /* SO_REUSE */
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, int);
+ *(int*)optval = ip_get_option(sock->conn->pcb.ip, optname);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, optname=0x%x, ..) = %s\n",
+ s, optname, (*(int*)optval?"on":"off")));
+ break;
+
+ case SO_TYPE:
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, *optlen, int);
+ switch (NETCONNTYPE_GROUP(netconn_type(sock->conn))) {
+ case NETCONN_RAW:
+ *(int*)optval = SOCK_RAW;
+ break;
+ case NETCONN_TCP:
+ *(int*)optval = SOCK_STREAM;
+ break;
+ case NETCONN_UDP:
+ *(int*)optval = SOCK_DGRAM;
+ break;
+ default: /* unrecognized socket type */
+ *(int*)optval = netconn_type(sock->conn);
+ LWIP_DEBUGF(SOCKETS_DEBUG,
+ ("lwip_getsockopt(%d, SOL_SOCKET, SO_TYPE): unrecognized socket type %d\n",
+ s, *(int *)optval));
+ } /* switch (netconn_type(sock->conn)) */
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, SO_TYPE) = %d\n",
+ s, *(int *)optval));
+ break;
+
+ case SO_ERROR:
+ LWIP_SOCKOPT_CHECK_OPTLEN(*optlen, int);
+ /* only overwrite ERR_OK or temporary errors */
+ if (((sock->err == 0) || (sock->err == EINPROGRESS)) && (sock->conn != NULL)) {
+ sock_set_errno(sock, err_to_errno(sock->conn->last_err));
+ }
+ *(int *)optval = (sock->err == 0xFF ? (int)-1 : (int)sock->err);
+ sock->err = 0;
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, SO_ERROR) = %d\n",
+ s, *(int *)optval));
+ break;
+
+#if LWIP_SO_SNDTIMEO
+ case SO_SNDTIMEO:
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, *optlen, LWIP_SO_SNDRCVTIMEO_OPTTYPE);
+ LWIP_SO_SNDRCVTIMEO_SET(optval, netconn_get_sendtimeout(sock->conn));
+ break;
+#endif /* LWIP_SO_SNDTIMEO */
+#if LWIP_SO_RCVTIMEO
+ case SO_RCVTIMEO:
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, *optlen, LWIP_SO_SNDRCVTIMEO_OPTTYPE);
+ LWIP_SO_SNDRCVTIMEO_SET(optval, netconn_get_recvtimeout(sock->conn));
+ break;
+#endif /* LWIP_SO_RCVTIMEO */
+#if LWIP_SO_RCVBUF
+ case SO_RCVBUF:
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, *optlen, int);
+ *(int *)optval = netconn_get_recvbufsize(sock->conn);
+ break;
+#endif /* LWIP_SO_RCVBUF */
+#if LWIP_SO_LINGER
+ case SO_LINGER:
+ {
+ s16_t conn_linger;
+ struct linger* linger = (struct linger*)optval;
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, *optlen, struct linger);
+ conn_linger = sock->conn->linger;
+ if (conn_linger >= 0) {
+ linger->l_onoff = 1;
+ linger->l_linger = (int)conn_linger;
+ } else {
+ linger->l_onoff = 0;
+ linger->l_linger = 0;
+ }
+ }
+ break;
+#endif /* LWIP_SO_LINGER */
+#if LWIP_UDP
+ case SO_NO_CHECK:
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, *optlen, int, NETCONN_UDP);
+#if LWIP_UDPLITE
+ if ((udp_flags(sock->conn->pcb.udp) & UDP_FLAGS_UDPLITE) != 0) {
+ /* this flag is only available for UDP, not for UDP lite */
+ return EAFNOSUPPORT;
+ }
+#endif /* LWIP_UDPLITE */
+ *(int*)optval = (udp_flags(sock->conn->pcb.udp) & UDP_FLAGS_NOCHKSUM) ? 1 : 0;
+ break;
+#endif /* LWIP_UDP*/
+ default:
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, UNIMPL: optname=0x%x, ..)\n",
+ s, optname));
+ err = ENOPROTOOPT;
+ break;
+ } /* switch (optname) */
+ break;
+
+/* Level: IPPROTO_IP */
+ case IPPROTO_IP:
+ switch (optname) {
+ case IP_TTL:
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, int);
+ *(int*)optval = sock->conn->pcb.ip->ttl;
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_TTL) = %d\n",
+ s, *(int *)optval));
+ break;
+ case IP_TOS:
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, int);
+ *(int*)optval = sock->conn->pcb.ip->tos;
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_TOS) = %d\n",
+ s, *(int *)optval));
+ break;
+#if LWIP_MULTICAST_TX_OPTIONS
+ case IP_MULTICAST_TTL:
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, u8_t);
+ if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) {
+ return ENOPROTOOPT;
+ }
+ *(u8_t*)optval = udp_get_multicast_ttl(sock->conn->pcb.udp);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_TTL) = %d\n",
+ s, *(int *)optval));
+ break;
+ case IP_MULTICAST_IF:
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, struct in_addr);
+ if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) {
+ return ENOPROTOOPT;
+ }
+ inet_addr_from_ip4addr((struct in_addr*)optval, udp_get_multicast_netif_addr(sock->conn->pcb.udp));
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_IF) = 0x%"X32_F"\n",
+ s, *(u32_t *)optval));
+ break;
+ case IP_MULTICAST_LOOP:
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, u8_t);
+ if ((sock->conn->pcb.udp->flags & UDP_FLAGS_MULTICAST_LOOP) != 0) {
+ *(u8_t*)optval = 1;
+ } else {
+ *(u8_t*)optval = 0;
+ }
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_LOOP) = %d\n",
+ s, *(int *)optval));
+ break;
+#endif /* LWIP_MULTICAST_TX_OPTIONS */
+ default:
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, UNIMPL: optname=0x%x, ..)\n",
+ s, optname));
+ err = ENOPROTOOPT;
+ break;
+ } /* switch (optname) */
+ break;
+
+#if LWIP_TCP
+/* Level: IPPROTO_TCP */
+ case IPPROTO_TCP:
+ /* Special case: all IPPROTO_TCP option take an int */
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, *optlen, int, NETCONN_TCP);
+ if (sock->conn->pcb.tcp->state == LISTEN) {
+ return EINVAL;
+ }
+ switch (optname) {
+ case TCP_NODELAY:
+ *(int*)optval = tcp_nagle_disabled(sock->conn->pcb.tcp);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, TCP_NODELAY) = %s\n",
+ s, (*(int*)optval)?"on":"off") );
+ break;
+ case TCP_KEEPALIVE:
+ *(int*)optval = (int)sock->conn->pcb.tcp->keep_idle;
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, TCP_KEEPALIVE) = %d\n",
+ s, *(int *)optval));
+ break;
+
+#if LWIP_TCP_KEEPALIVE
+ case TCP_KEEPIDLE:
+ *(int*)optval = (int)(sock->conn->pcb.tcp->keep_idle/1000);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, TCP_KEEPIDLE) = %d\n",
+ s, *(int *)optval));
+ break;
+ case TCP_KEEPINTVL:
+ *(int*)optval = (int)(sock->conn->pcb.tcp->keep_intvl/1000);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, TCP_KEEPINTVL) = %d\n",
+ s, *(int *)optval));
+ break;
+ case TCP_KEEPCNT:
+ *(int*)optval = (int)sock->conn->pcb.tcp->keep_cnt;
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, TCP_KEEPCNT) = %d\n",
+ s, *(int *)optval));
+ break;
+#endif /* LWIP_TCP_KEEPALIVE */
+ default:
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, UNIMPL: optname=0x%x, ..)\n",
+ s, optname));
+ err = ENOPROTOOPT;
+ break;
+ } /* switch (optname) */
+ break;
+#endif /* LWIP_TCP */
+
+#if LWIP_IPV6
+/* Level: IPPROTO_IPV6 */
+ case IPPROTO_IPV6:
+ switch (optname) {
+ case IPV6_V6ONLY:
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, *optlen, int);
+ *(int*)optval = (netconn_get_ipv6only(sock->conn) ? 1 : 0);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IPV6, IPV6_V6ONLY) = %d\n",
+ s, *(int *)optval));
+ break;
+ default:
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IPV6, UNIMPL: optname=0x%x, ..)\n",
+ s, optname));
+ err = ENOPROTOOPT;
+ break;
+ } /* switch (optname) */
+ break;
+#endif /* LWIP_IPV6 */
+
+#if LWIP_UDP && LWIP_UDPLITE
+ /* Level: IPPROTO_UDPLITE */
+ case IPPROTO_UDPLITE:
+ /* Special case: all IPPROTO_UDPLITE option take an int */
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, int);
+ /* If this is no UDP lite socket, ignore any options. */
+ if (!NETCONNTYPE_ISUDPLITE(netconn_type(sock->conn))) {
+ return ENOPROTOOPT;
+ }
+ switch (optname) {
+ case UDPLITE_SEND_CSCOV:
+ *(int*)optval = sock->conn->pcb.udp->chksum_len_tx;
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_UDPLITE, UDPLITE_SEND_CSCOV) = %d\n",
+ s, (*(int*)optval)) );
+ break;
+ case UDPLITE_RECV_CSCOV:
+ *(int*)optval = sock->conn->pcb.udp->chksum_len_rx;
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_UDPLITE, UDPLITE_RECV_CSCOV) = %d\n",
+ s, (*(int*)optval)) );
+ break;
+ default:
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_UDPLITE, UNIMPL: optname=0x%x, ..)\n",
+ s, optname));
+ err = ENOPROTOOPT;
+ break;
+ } /* switch (optname) */
+ break;
+#endif /* LWIP_UDP */
+ /* Level: IPPROTO_RAW */
+ case IPPROTO_RAW:
+ switch (optname) {
+#if LWIP_IPV6 && LWIP_RAW
+ case IPV6_CHECKSUM:
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, *optlen, int, NETCONN_RAW);
+ if (sock->conn->pcb.raw->chksum_reqd == 0) {
+ *(int *)optval = -1;
+ } else {
+ *(int *)optval = sock->conn->pcb.raw->chksum_offset;
+ }
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_RAW, IPV6_CHECKSUM) = %d\n",
+ s, (*(int*)optval)) );
+ break;
+#endif /* LWIP_IPV6 && LWIP_RAW */
+ default:
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_RAW, UNIMPL: optname=0x%x, ..)\n",
+ s, optname));
+ err = ENOPROTOOPT;
+ break;
+ } /* switch (optname) */
+ break;
+ default:
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, level=0x%x, UNIMPL: optname=0x%x, ..)\n",
+ s, level, optname));
+ err = ENOPROTOOPT;
+ break;
+ } /* switch (level) */
+
+ return err;
+}
+
+int
+lwip_setsockopt(int s, int level, int optname, const void *optval, socklen_t optlen)
+{
+ u8_t err = 0;
+ struct lwip_sock *sock = get_socket(s);
+#if !LWIP_TCPIP_CORE_LOCKING
+ LWIP_SETGETSOCKOPT_DATA_VAR_DECLARE(data);
+#endif /* !LWIP_TCPIP_CORE_LOCKING */
+
+ if (!sock) {
+ return -1;
+ }
+
+ if (NULL == optval) {
+ sock_set_errno(sock, EFAULT);
+ return -1;
+ }
+
+#if LWIP_TCPIP_CORE_LOCKING
+ /* core-locking can just call the -impl function */
+ LOCK_TCPIP_CORE();
+ err = lwip_setsockopt_impl(s, level, optname, optval, optlen);
+ UNLOCK_TCPIP_CORE();
+
+#else /* LWIP_TCPIP_CORE_LOCKING */
+
+#if LWIP_MPU_COMPATIBLE
+ /* MPU_COMPATIBLE copies the optval data, so check for max size here */
+ if (optlen > LWIP_SETGETSOCKOPT_MAXOPTLEN) {
+ sock_set_errno(sock, ENOBUFS);
+ return -1;
+ }
+#endif /* LWIP_MPU_COMPATIBLE */
+
+ LWIP_SETGETSOCKOPT_DATA_VAR_ALLOC(data, sock);
+ LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).s = s;
+ LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).level = level;
+ LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optname = optname;
+ LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optlen = optlen;
+#if LWIP_MPU_COMPATIBLE
+ MEMCPY(LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optval, optval, optlen);
+#else /* LWIP_MPU_COMPATIBLE */
+ LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optval.pc = (const void*)optval;
+#endif /* LWIP_MPU_COMPATIBLE */
+ LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).err = 0;
+#if LWIP_NETCONN_SEM_PER_THREAD
+ LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem = LWIP_NETCONN_THREAD_SEM_GET();
+#else
+ LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem = &sock->conn->op_completed;
+#endif
+ err = tcpip_callback(lwip_setsockopt_callback, &LWIP_SETGETSOCKOPT_DATA_VAR_REF(data));
+ if (err != ERR_OK) {
+ LWIP_SETGETSOCKOPT_DATA_VAR_FREE(data);
+ sock_set_errno(sock, err_to_errno(err));
+ return -1;
+ }
+ sys_arch_sem_wait((sys_sem_t*)(LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem), 0);
+
+ /* maybe lwip_getsockopt_internal has changed err */
+ err = LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).err;
+ LWIP_SETGETSOCKOPT_DATA_VAR_FREE(data);
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+
+ sock_set_errno(sock, err);
+ return err ? -1 : 0;
+}
+
+#if !LWIP_TCPIP_CORE_LOCKING
+/** lwip_setsockopt_callback: only used without CORE_LOCKING
+ * to get into the tcpip_thread
+ */
+static void
+lwip_setsockopt_callback(void *arg)
+{
+ struct lwip_setgetsockopt_data *data;
+ LWIP_ASSERT("arg != NULL", arg != NULL);
+ data = (struct lwip_setgetsockopt_data*)arg;
+
+ data->err = lwip_setsockopt_impl(data->s, data->level, data->optname,
+#if LWIP_MPU_COMPATIBLE
+ data->optval,
+#else /* LWIP_MPU_COMPATIBLE */
+ data->optval.pc,
+#endif /* LWIP_MPU_COMPATIBLE */
+ data->optlen);
+
+ sys_sem_signal((sys_sem_t*)(data->completed_sem));
+}
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+
+/** lwip_setsockopt_impl: the actual implementation of setsockopt:
+ * same argument as lwip_setsockopt, either called directly or through callback
+ */
+static u8_t
+lwip_setsockopt_impl(int s, int level, int optname, const void *optval, socklen_t optlen)
+{
+ u8_t err = 0;
+ struct lwip_sock *sock = tryget_socket(s);
+ if (!sock) {
+ return EBADF;
+ }
+
+ switch (level) {
+
+/* Level: SOL_SOCKET */
+ case SOL_SOCKET:
+ switch (optname) {
+
+ /* SO_ACCEPTCONN is get-only */
+
+ /* The option flags */
+ case SO_BROADCAST:
+ case SO_KEEPALIVE:
+#if SO_REUSE
+ case SO_REUSEADDR:
+#endif /* SO_REUSE */
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, int);
+ if (*(const int*)optval) {
+ ip_set_option(sock->conn->pcb.ip, optname);
+ } else {
+ ip_reset_option(sock->conn->pcb.ip, optname);
+ }
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, SOL_SOCKET, optname=0x%x, ..) -> %s\n",
+ s, optname, (*(const int*)optval?"on":"off")));
+ break;
+
+ /* SO_TYPE is get-only */
+ /* SO_ERROR is get-only */
+
+#if LWIP_SO_SNDTIMEO
+ case SO_SNDTIMEO:
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, optlen, LWIP_SO_SNDRCVTIMEO_OPTTYPE);
+ netconn_set_sendtimeout(sock->conn, LWIP_SO_SNDRCVTIMEO_GET_MS(optval));
+ break;
+#endif /* LWIP_SO_SNDTIMEO */
+#if LWIP_SO_RCVTIMEO
+ case SO_RCVTIMEO:
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, optlen, LWIP_SO_SNDRCVTIMEO_OPTTYPE);
+ netconn_set_recvtimeout(sock->conn, (int)LWIP_SO_SNDRCVTIMEO_GET_MS(optval));
+ break;
+#endif /* LWIP_SO_RCVTIMEO */
+#if LWIP_SO_RCVBUF
+ case SO_RCVBUF:
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, optlen, int);
+ netconn_set_recvbufsize(sock->conn, *(const int*)optval);
+ break;
+#endif /* LWIP_SO_RCVBUF */
+#if LWIP_SO_LINGER
+ case SO_LINGER:
+ {
+ const struct linger* linger = (const struct linger*)optval;
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, optlen, struct linger);
+ if (linger->l_onoff) {
+ int lingersec = linger->l_linger;
+ if (lingersec < 0) {
+ return EINVAL;
+ }
+ if (lingersec > 0xFFFF) {
+ lingersec = 0xFFFF;
+ }
+ sock->conn->linger = (s16_t)lingersec;
+ } else {
+ sock->conn->linger = -1;
+ }
+ }
+ break;
+#endif /* LWIP_SO_LINGER */
+#if LWIP_UDP
+ case SO_NO_CHECK:
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, int, NETCONN_UDP);
+#if LWIP_UDPLITE
+ if ((udp_flags(sock->conn->pcb.udp) & UDP_FLAGS_UDPLITE) != 0) {
+ /* this flag is only available for UDP, not for UDP lite */
+ return EAFNOSUPPORT;
+ }
+#endif /* LWIP_UDPLITE */
+ if (*(const int*)optval) {
+ udp_setflags(sock->conn->pcb.udp, udp_flags(sock->conn->pcb.udp) | UDP_FLAGS_NOCHKSUM);
+ } else {
+ udp_setflags(sock->conn->pcb.udp, udp_flags(sock->conn->pcb.udp) & ~UDP_FLAGS_NOCHKSUM);
+ }
+ break;
+#endif /* LWIP_UDP */
+ default:
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, SOL_SOCKET, UNIMPL: optname=0x%x, ..)\n",
+ s, optname));
+ err = ENOPROTOOPT;
+ break;
+ } /* switch (optname) */
+ break;
+
+/* Level: IPPROTO_IP */
+ case IPPROTO_IP:
+ switch (optname) {
+ case IP_TTL:
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, int);
+ sock->conn->pcb.ip->ttl = (u8_t)(*(const int*)optval);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, IP_TTL, ..) -> %d\n",
+ s, sock->conn->pcb.ip->ttl));
+ break;
+ case IP_TOS:
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, int);
+ sock->conn->pcb.ip->tos = (u8_t)(*(const int*)optval);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, IP_TOS, ..)-> %d\n",
+ s, sock->conn->pcb.ip->tos));
+ break;
+#if LWIP_MULTICAST_TX_OPTIONS
+ case IP_MULTICAST_TTL:
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, u8_t, NETCONN_UDP);
+ udp_set_multicast_ttl(sock->conn->pcb.udp, (u8_t)(*(const u8_t*)optval));
+ break;
+ case IP_MULTICAST_IF:
+ {
+ ip4_addr_t if_addr;
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, struct in_addr, NETCONN_UDP);
+ inet_addr_to_ip4addr(&if_addr, (const struct in_addr*)optval);
+ udp_set_multicast_netif_addr(sock->conn->pcb.udp, &if_addr);
+ }
+ break;
+ case IP_MULTICAST_LOOP:
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, u8_t, NETCONN_UDP);
+ if (*(const u8_t*)optval) {
+ udp_setflags(sock->conn->pcb.udp, udp_flags(sock->conn->pcb.udp) | UDP_FLAGS_MULTICAST_LOOP);
+ } else {
+ udp_setflags(sock->conn->pcb.udp, udp_flags(sock->conn->pcb.udp) & ~UDP_FLAGS_MULTICAST_LOOP);
+ }
+ break;
+#endif /* LWIP_MULTICAST_TX_OPTIONS */
+#if LWIP_IGMP
+ case IP_ADD_MEMBERSHIP:
+ case IP_DROP_MEMBERSHIP:
+ {
+ /* If this is a TCP or a RAW socket, ignore these options. */
+ /* @todo: assign membership to this socket so that it is dropped when closing the socket */
+ err_t igmp_err;
+ const struct ip_mreq *imr = (const struct ip_mreq *)optval;
+ ip4_addr_t if_addr;
+ ip4_addr_t multi_addr;
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, struct ip_mreq, NETCONN_UDP);
+ inet_addr_to_ip4addr(&if_addr, &imr->imr_interface);
+ inet_addr_to_ip4addr(&multi_addr, &imr->imr_multiaddr);
+ if (optname == IP_ADD_MEMBERSHIP) {
+ if (!lwip_socket_register_membership(s, &if_addr, &multi_addr)) {
+ /* cannot track membership (out of memory) */
+ err = ENOMEM;
+ igmp_err = ERR_OK;
+ } else {
+ igmp_err = igmp_joingroup(&if_addr, &multi_addr);
+ }
+ } else {
+ igmp_err = igmp_leavegroup(&if_addr, &multi_addr);
+ lwip_socket_unregister_membership(s, &if_addr, &multi_addr);
+ }
+ if (igmp_err != ERR_OK) {
+ err = EADDRNOTAVAIL;
+ }
+ }
+ break;
+#endif /* LWIP_IGMP */
+ default:
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, UNIMPL: optname=0x%x, ..)\n",
+ s, optname));
+ err = ENOPROTOOPT;
+ break;
+ } /* switch (optname) */
+ break;
+
+#if LWIP_TCP
+/* Level: IPPROTO_TCP */
+ case IPPROTO_TCP:
+ /* Special case: all IPPROTO_TCP option take an int */
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, int, NETCONN_TCP);
+ if (sock->conn->pcb.tcp->state == LISTEN) {
+ return EINVAL;
+ }
+ switch (optname) {
+ case TCP_NODELAY:
+ if (*(const int*)optval) {
+ tcp_nagle_disable(sock->conn->pcb.tcp);
+ } else {
+ tcp_nagle_enable(sock->conn->pcb.tcp);
+ }
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_NODELAY) -> %s\n",
+ s, (*(const int *)optval)?"on":"off") );
+ break;
+ case TCP_KEEPALIVE:
+ sock->conn->pcb.tcp->keep_idle = (u32_t)(*(const int*)optval);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPALIVE) -> %"U32_F"\n",
+ s, sock->conn->pcb.tcp->keep_idle));
+ break;
+
+#if LWIP_TCP_KEEPALIVE
+ case TCP_KEEPIDLE:
+ sock->conn->pcb.tcp->keep_idle = 1000*(u32_t)(*(const int*)optval);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPIDLE) -> %"U32_F"\n",
+ s, sock->conn->pcb.tcp->keep_idle));
+ break;
+ case TCP_KEEPINTVL:
+ sock->conn->pcb.tcp->keep_intvl = 1000*(u32_t)(*(const int*)optval);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPINTVL) -> %"U32_F"\n",
+ s, sock->conn->pcb.tcp->keep_intvl));
+ break;
+ case TCP_KEEPCNT:
+ sock->conn->pcb.tcp->keep_cnt = (u32_t)(*(const int*)optval);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPCNT) -> %"U32_F"\n",
+ s, sock->conn->pcb.tcp->keep_cnt));
+ break;
+#endif /* LWIP_TCP_KEEPALIVE */
+ default:
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, UNIMPL: optname=0x%x, ..)\n",
+ s, optname));
+ err = ENOPROTOOPT;
+ break;
+ } /* switch (optname) */
+ break;
+#endif /* LWIP_TCP*/
+
+#if LWIP_IPV6
+/* Level: IPPROTO_IPV6 */
+ case IPPROTO_IPV6:
+ switch (optname) {
+ case IPV6_V6ONLY:
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, int, NETCONN_TCP);
+ if (*(const int*)optval) {
+ netconn_set_ipv6only(sock->conn, 1);
+ } else {
+ netconn_set_ipv6only(sock->conn, 0);
+ }
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IPV6, IPV6_V6ONLY, ..) -> %d\n",
+ s, (netconn_get_ipv6only(sock->conn) ? 1 : 0)));
+ break;
+ default:
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IPV6, UNIMPL: optname=0x%x, ..)\n",
+ s, optname));
+ err = ENOPROTOOPT;
+ break;
+ } /* switch (optname) */
+ break;
+#endif /* LWIP_IPV6 */
+
+#if LWIP_UDP && LWIP_UDPLITE
+ /* Level: IPPROTO_UDPLITE */
+ case IPPROTO_UDPLITE:
+ /* Special case: all IPPROTO_UDPLITE option take an int */
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, int);
+ /* If this is no UDP lite socket, ignore any options. */
+ if (!NETCONNTYPE_ISUDPLITE(netconn_type(sock->conn))) {
+ return ENOPROTOOPT;
+ }
+ switch (optname) {
+ case UDPLITE_SEND_CSCOV:
+ if ((*(const int*)optval != 0) && ((*(const int*)optval < 8) || (*(const int*)optval > 0xffff))) {
+ /* don't allow illegal values! */
+ sock->conn->pcb.udp->chksum_len_tx = 8;
+ } else {
+ sock->conn->pcb.udp->chksum_len_tx = (u16_t)*(const int*)optval;
+ }
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_UDPLITE, UDPLITE_SEND_CSCOV) -> %d\n",
+ s, (*(const int*)optval)) );
+ break;
+ case UDPLITE_RECV_CSCOV:
+ if ((*(const int*)optval != 0) && ((*(const int*)optval < 8) || (*(const int*)optval > 0xffff))) {
+ /* don't allow illegal values! */
+ sock->conn->pcb.udp->chksum_len_rx = 8;
+ } else {
+ sock->conn->pcb.udp->chksum_len_rx = (u16_t)*(const int*)optval;
+ }
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_UDPLITE, UDPLITE_RECV_CSCOV) -> %d\n",
+ s, (*(const int*)optval)) );
+ break;
+ default:
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_UDPLITE, UNIMPL: optname=0x%x, ..)\n",
+ s, optname));
+ err = ENOPROTOOPT;
+ break;
+ } /* switch (optname) */
+ break;
+#endif /* LWIP_UDP */
+ /* Level: IPPROTO_RAW */
+ case IPPROTO_RAW:
+ switch (optname) {
+#if LWIP_IPV6 && LWIP_RAW
+ case IPV6_CHECKSUM:
+ /* It should not be possible to disable the checksum generation with ICMPv6
+ * as per RFC 3542 chapter 3.1 */
+ if(sock->conn->pcb.raw->protocol == IPPROTO_ICMPV6) {
+ return EINVAL;
+ }
+
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, int, NETCONN_RAW);
+ if (*(const int *)optval < 0) {
+ sock->conn->pcb.raw->chksum_reqd = 0;
+ } else if (*(const int *)optval & 1) {
+ /* Per RFC3542, odd offsets are not allowed */
+ return EINVAL;
+ } else {
+ sock->conn->pcb.raw->chksum_reqd = 1;
+ sock->conn->pcb.raw->chksum_offset = (u16_t)*(const int *)optval;
+ }
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_RAW, IPV6_CHECKSUM, ..) -> %d\n",
+ s, sock->conn->pcb.raw->chksum_reqd));
+ break;
+#endif /* LWIP_IPV6 && LWIP_RAW */
+ default:
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_RAW, UNIMPL: optname=0x%x, ..)\n",
+ s, optname));
+ err = ENOPROTOOPT;
+ break;
+ } /* switch (optname) */
+ break;
+ default:
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, level=0x%x, UNIMPL: optname=0x%x, ..)\n",
+ s, level, optname));
+ err = ENOPROTOOPT;
+ break;
+ } /* switch (level) */
+
+ return err;
+}
+
+int
+lwip_ioctl(int s, long cmd, void *argp)
+{
+ struct lwip_sock *sock = get_socket(s);
+ u8_t val;
+#if LWIP_SO_RCVBUF
+ u16_t buflen = 0;
+ int recv_avail;
+#endif /* LWIP_SO_RCVBUF */
+
+ if (!sock) {
+ return -1;
+ }
+
+ switch (cmd) {
+#if LWIP_SO_RCVBUF || LWIP_FIONREAD_LINUXMODE
+ case FIONREAD:
+ if (!argp) {
+ sock_set_errno(sock, EINVAL);
+ return -1;
+ }
+#if LWIP_FIONREAD_LINUXMODE
+ if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) {
+ struct pbuf *p;
+ if (sock->lastdata) {
+ p = ((struct netbuf *)sock->lastdata)->p;
+ *((int*)argp) = p->tot_len - sock->lastoffset;
+ } else {
+ struct netbuf *rxbuf;
+ err_t err;
+ if (sock->rcvevent <= 0) {
+ *((int*)argp) = 0;
+ } else {
+ err = netconn_recv(sock->conn, &rxbuf);
+ if (err != ERR_OK) {
+ *((int*)argp) = 0;
+ } else {
+ sock->lastdata = rxbuf;
+ sock->lastoffset = 0;
+ *((int*)argp) = rxbuf->p->tot_len;
+ }
+ }
+ }
+ return 0;
+ }
+#endif /* LWIP_FIONREAD_LINUXMODE */
+
+#if LWIP_SO_RCVBUF
+ /* we come here if either LWIP_FIONREAD_LINUXMODE==0 or this is a TCP socket */
+ SYS_ARCH_GET(sock->conn->recv_avail, recv_avail);
+ if (recv_avail < 0) {
+ recv_avail = 0;
+ }
+ *((int*)argp) = recv_avail;
+
+ /* Check if there is data left from the last recv operation. /maq 041215 */
+ if (sock->lastdata) {
+ struct pbuf *p = (struct pbuf *)sock->lastdata;
+ if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) {
+ p = ((struct netbuf *)p)->p;
+ }
+ buflen = p->tot_len;
+ buflen -= sock->lastoffset;
+
+ *((int*)argp) += buflen;
+ }
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_ioctl(%d, FIONREAD, %p) = %"U16_F"\n", s, argp, *((u16_t*)argp)));
+ sock_set_errno(sock, 0);
+ return 0;
+#else /* LWIP_SO_RCVBUF */
+ break;
+#endif /* LWIP_SO_RCVBUF */
+#endif /* LWIP_SO_RCVBUF || LWIP_FIONREAD_LINUXMODE */
+
+ case (long)FIONBIO:
+ val = 0;
+ if (argp && *(u32_t*)argp) {
+ val = 1;
+ }
+ netconn_set_nonblocking(sock->conn, val);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_ioctl(%d, FIONBIO, %d)\n", s, val));
+ sock_set_errno(sock, 0);
+ return 0;
+
+ default:
+ break;
+ } /* switch (cmd) */
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_ioctl(%d, UNIMPL: 0x%lx, %p)\n", s, cmd, argp));
+ sock_set_errno(sock, ENOSYS); /* not yet implemented */
+ return -1;
+}
+
+/** A minimal implementation of fcntl.
+ * Currently only the commands F_GETFL and F_SETFL are implemented.
+ * Only the flag O_NONBLOCK is implemented.
+ */
+int
+lwip_fcntl(int s, int cmd, int val)
+{
+ struct lwip_sock *sock = get_socket(s);
+ int ret = -1;
+
+ if (!sock) {
+ return -1;
+ }
+
+ switch (cmd) {
+ case F_GETFL:
+ ret = netconn_is_nonblocking(sock->conn) ? O_NONBLOCK : 0;
+ sock_set_errno(sock, 0);
+ break;
+ case F_SETFL:
+ if ((val & ~O_NONBLOCK) == 0) {
+ /* only O_NONBLOCK, all other bits are zero */
+ netconn_set_nonblocking(sock->conn, val & O_NONBLOCK);
+ ret = 0;
+ sock_set_errno(sock, 0);
+ } else {
+ sock_set_errno(sock, ENOSYS); /* not yet implemented */
+ }
+ break;
+ default:
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_fcntl(%d, UNIMPL: %d, %d)\n", s, cmd, val));
+ sock_set_errno(sock, ENOSYS); /* not yet implemented */
+ break;
+ }
+ return ret;
+}
+
+#if LWIP_IGMP
+/** Register a new IGMP membership. On socket close, the membership is dropped automatically.
+ *
+ * ATTENTION: this function is called from tcpip_thread (or under CORE_LOCK).
+ *
+ * @return 1 on success, 0 on failure
+ */
+static int
+lwip_socket_register_membership(int s, const ip4_addr_t *if_addr, const ip4_addr_t *multi_addr)
+{
+ struct lwip_sock *sock = get_socket(s);
+ int i;
+
+ if (!sock) {
+ return 0;
+ }
+
+ for (i = 0; i < LWIP_SOCKET_MAX_MEMBERSHIPS; i++) {
+ if (socket_ipv4_multicast_memberships[i].sock == NULL) {
+ socket_ipv4_multicast_memberships[i].sock = sock;
+ ip4_addr_copy(socket_ipv4_multicast_memberships[i].if_addr, *if_addr);
+ ip4_addr_copy(socket_ipv4_multicast_memberships[i].multi_addr, *multi_addr);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/** Unregister a previously registered membership. This prevents dropping the membership
+ * on socket close.
+ *
+ * ATTENTION: this function is called from tcpip_thread (or under CORE_LOCK).
+ */
+static void
+lwip_socket_unregister_membership(int s, const ip4_addr_t *if_addr, const ip4_addr_t *multi_addr)
+{
+ struct lwip_sock *sock = get_socket(s);
+ int i;
+
+ if (!sock) {
+ return;
+ }
+
+ for (i = 0; i < LWIP_SOCKET_MAX_MEMBERSHIPS; i++) {
+ if ((socket_ipv4_multicast_memberships[i].sock == sock) &&
+ ip4_addr_cmp(&socket_ipv4_multicast_memberships[i].if_addr, if_addr) &&
+ ip4_addr_cmp(&socket_ipv4_multicast_memberships[i].multi_addr, multi_addr)) {
+ socket_ipv4_multicast_memberships[i].sock = NULL;
+ ip4_addr_set_zero(&socket_ipv4_multicast_memberships[i].if_addr);
+ ip4_addr_set_zero(&socket_ipv4_multicast_memberships[i].multi_addr);
+ return;
+ }
+ }
+}
+
+/** Drop all memberships of a socket that were not dropped explicitly via setsockopt.
+ *
+ * ATTENTION: this function is NOT called from tcpip_thread (or under CORE_LOCK).
+ */
+static void
+lwip_socket_drop_registered_memberships(int s)
+{
+ struct lwip_sock *sock = get_socket(s);
+ int i;
+
+ if (!sock) {
+ return;
+ }
+
+ for (i = 0; i < LWIP_SOCKET_MAX_MEMBERSHIPS; i++) {
+ if (socket_ipv4_multicast_memberships[i].sock == sock) {
+ ip_addr_t multi_addr, if_addr;
+ ip_addr_copy_from_ip4(multi_addr, socket_ipv4_multicast_memberships[i].multi_addr);
+ ip_addr_copy_from_ip4(if_addr, socket_ipv4_multicast_memberships[i].if_addr);
+ socket_ipv4_multicast_memberships[i].sock = NULL;
+ ip4_addr_set_zero(&socket_ipv4_multicast_memberships[i].if_addr);
+ ip4_addr_set_zero(&socket_ipv4_multicast_memberships[i].multi_addr);
+
+ netconn_join_leave_group(sock->conn, &multi_addr, &if_addr, NETCONN_LEAVE);
+ }
+ }
+}
+#endif /* LWIP_IGMP */
+#endif /* LWIP_SOCKET */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/api/tcpip.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/api/tcpip.c
new file mode 100644
index 0000000..07b2f98
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/api/tcpip.c
@@ -0,0 +1,518 @@
+/**
+ * @file
+ * Sequential API Main thread module
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if !NO_SYS /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/priv/tcpip_priv.h"
+#include "lwip/sys.h"
+#include "lwip/memp.h"
+#include "lwip/mem.h"
+#include "lwip/init.h"
+#include "lwip/ip.h"
+#include "lwip/pbuf.h"
+#include "lwip/etharp.h"
+#include "netif/ethernet.h"
+
+#define TCPIP_MSG_VAR_REF(name) API_VAR_REF(name)
+#define TCPIP_MSG_VAR_DECLARE(name) API_VAR_DECLARE(struct tcpip_msg, name)
+#define TCPIP_MSG_VAR_ALLOC(name) API_VAR_ALLOC(struct tcpip_msg, MEMP_TCPIP_MSG_API, name, ERR_MEM)
+#define TCPIP_MSG_VAR_FREE(name) API_VAR_FREE(MEMP_TCPIP_MSG_API, name)
+
+/* global variables */
+static tcpip_init_done_fn tcpip_init_done;
+static void *tcpip_init_done_arg;
+static sys_mbox_t mbox;
+
+#if LWIP_TCPIP_CORE_LOCKING
+/** The global semaphore to lock the stack. */
+sys_mutex_t lock_tcpip_core;
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+
+#if LWIP_TIMERS
+/* wait for a message, timeouts are processed while waiting */
+#define TCPIP_MBOX_FETCH(mbox, msg) sys_timeouts_mbox_fetch(mbox, msg)
+#else /* LWIP_TIMERS */
+/* wait for a message with timers disabled (e.g. pass a timer-check trigger into tcpip_thread) */
+#define TCPIP_MBOX_FETCH(mbox, msg) sys_mbox_fetch(mbox, msg)
+#endif /* LWIP_TIMERS */
+
+/**
+ * The main lwIP thread. This thread has exclusive access to lwIP core functions
+ * (unless access to them is not locked). Other threads communicate with this
+ * thread using message boxes.
+ *
+ * It also starts all the timers to make sure they are running in the right
+ * thread context.
+ *
+ * @param arg unused argument
+ */
+static void
+tcpip_thread(void *arg)
+{
+ struct tcpip_msg *msg;
+ LWIP_UNUSED_ARG(arg);
+
+ if (tcpip_init_done != NULL) {
+ tcpip_init_done(tcpip_init_done_arg);
+ }
+
+ LOCK_TCPIP_CORE();
+ while (1) { /* MAIN Loop */
+ UNLOCK_TCPIP_CORE();
+ LWIP_TCPIP_THREAD_ALIVE();
+ /* wait for a message, timeouts are processed while waiting */
+ TCPIP_MBOX_FETCH(&mbox, (void **)&msg);
+ LOCK_TCPIP_CORE();
+ if (msg == NULL) {
+ LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: invalid message: NULL\n"));
+ LWIP_ASSERT("tcpip_thread: invalid message", 0);
+ continue;
+ }
+ switch (msg->type) {
+#if !LWIP_TCPIP_CORE_LOCKING
+ case TCPIP_MSG_API:
+ LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: API message %p\n", (void *)msg));
+ msg->msg.api_msg.function(msg->msg.api_msg.msg);
+ break;
+ case TCPIP_MSG_API_CALL:
+ LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: API CALL message %p\n", (void *)msg));
+ msg->msg.api_call.arg->err = msg->msg.api_call.function(msg->msg.api_call.arg);
+ sys_sem_signal(msg->msg.api_call.sem);
+ break;
+#endif /* !LWIP_TCPIP_CORE_LOCKING */
+
+#if !LWIP_TCPIP_CORE_LOCKING_INPUT
+ case TCPIP_MSG_INPKT:
+ LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: PACKET %p\n", (void *)msg));
+ msg->msg.inp.input_fn(msg->msg.inp.p, msg->msg.inp.netif);
+ memp_free(MEMP_TCPIP_MSG_INPKT, msg);
+ break;
+#endif /* !LWIP_TCPIP_CORE_LOCKING_INPUT */
+
+#if LWIP_TCPIP_TIMEOUT && LWIP_TIMERS
+ case TCPIP_MSG_TIMEOUT:
+ LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: TIMEOUT %p\n", (void *)msg));
+ sys_timeout(msg->msg.tmo.msecs, msg->msg.tmo.h, msg->msg.tmo.arg);
+ memp_free(MEMP_TCPIP_MSG_API, msg);
+ break;
+ case TCPIP_MSG_UNTIMEOUT:
+ LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: UNTIMEOUT %p\n", (void *)msg));
+ sys_untimeout(msg->msg.tmo.h, msg->msg.tmo.arg);
+ memp_free(MEMP_TCPIP_MSG_API, msg);
+ break;
+#endif /* LWIP_TCPIP_TIMEOUT && LWIP_TIMERS */
+
+ case TCPIP_MSG_CALLBACK:
+ LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: CALLBACK %p\n", (void *)msg));
+ msg->msg.cb.function(msg->msg.cb.ctx);
+ memp_free(MEMP_TCPIP_MSG_API, msg);
+ break;
+
+ case TCPIP_MSG_CALLBACK_STATIC:
+ LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: CALLBACK_STATIC %p\n", (void *)msg));
+ msg->msg.cb.function(msg->msg.cb.ctx);
+ break;
+
+ default:
+ LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: invalid message: %d\n", msg->type));
+ LWIP_ASSERT("tcpip_thread: invalid message", 0);
+ break;
+ }
+ }
+}
+
+/**
+ * Pass a received packet to tcpip_thread for input processing
+ *
+ * @param p the received packet
+ * @param inp the network interface on which the packet was received
+ * @param input_fn input function to call
+ */
+err_t
+tcpip_inpkt(struct pbuf *p, struct netif *inp, netif_input_fn input_fn)
+{
+#if LWIP_TCPIP_CORE_LOCKING_INPUT
+ err_t ret;
+ LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_inpkt: PACKET %p/%p\n", (void *)p, (void *)inp));
+ LOCK_TCPIP_CORE();
+ ret = input_fn(p, inp);
+ UNLOCK_TCPIP_CORE();
+ return ret;
+#else /* LWIP_TCPIP_CORE_LOCKING_INPUT */
+ struct tcpip_msg *msg;
+
+ LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(mbox));
+
+ msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_INPKT);
+ if (msg == NULL) {
+ return ERR_MEM;
+ }
+
+ msg->type = TCPIP_MSG_INPKT;
+ msg->msg.inp.p = p;
+ msg->msg.inp.netif = inp;
+ msg->msg.inp.input_fn = input_fn;
+ if (sys_mbox_trypost(&mbox, msg) != ERR_OK) {
+ memp_free(MEMP_TCPIP_MSG_INPKT, msg);
+ return ERR_MEM;
+ }
+ return ERR_OK;
+#endif /* LWIP_TCPIP_CORE_LOCKING_INPUT */
+}
+
+/**
+ * @ingroup lwip_os
+ * Pass a received packet to tcpip_thread for input processing with
+ * ethernet_input or ip_input. Don't call directly, pass to netif_add()
+ * and call netif->input().
+ *
+ * @param p the received packet, p->payload pointing to the Ethernet header or
+ * to an IP header (if inp doesn't have NETIF_FLAG_ETHARP or
+ * NETIF_FLAG_ETHERNET flags)
+ * @param inp the network interface on which the packet was received
+ */
+err_t
+tcpip_input(struct pbuf *p, struct netif *inp)
+{
+#if LWIP_ETHERNET
+ if (inp->flags & (NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET)) {
+ return tcpip_inpkt(p, inp, ethernet_input);
+ } else
+#endif /* LWIP_ETHERNET */
+ return tcpip_inpkt(p, inp, ip_input);
+}
+
+/**
+ * Call a specific function in the thread context of
+ * tcpip_thread for easy access synchronization.
+ * A function called in that way may access lwIP core code
+ * without fearing concurrent access.
+ *
+ * @param function the function to call
+ * @param ctx parameter passed to f
+ * @param block 1 to block until the request is posted, 0 to non-blocking mode
+ * @return ERR_OK if the function was called, another err_t if not
+ */
+err_t
+tcpip_callback_with_block(tcpip_callback_fn function, void *ctx, u8_t block)
+{
+ struct tcpip_msg *msg;
+
+ LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(mbox));
+
+ msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API);
+ if (msg == NULL) {
+ return ERR_MEM;
+ }
+
+ msg->type = TCPIP_MSG_CALLBACK;
+ msg->msg.cb.function = function;
+ msg->msg.cb.ctx = ctx;
+ if (block) {
+ sys_mbox_post(&mbox, msg);
+ } else {
+ if (sys_mbox_trypost(&mbox, msg) != ERR_OK) {
+ memp_free(MEMP_TCPIP_MSG_API, msg);
+ return ERR_MEM;
+ }
+ }
+ return ERR_OK;
+}
+
+#if LWIP_TCPIP_TIMEOUT && LWIP_TIMERS
+/**
+ * call sys_timeout in tcpip_thread
+ *
+ * @param msecs time in milliseconds for timeout
+ * @param h function to be called on timeout
+ * @param arg argument to pass to timeout function h
+ * @return ERR_MEM on memory error, ERR_OK otherwise
+ */
+err_t
+tcpip_timeout(u32_t msecs, sys_timeout_handler h, void *arg)
+{
+ struct tcpip_msg *msg;
+
+ LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(mbox));
+
+ msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API);
+ if (msg == NULL) {
+ return ERR_MEM;
+ }
+
+ msg->type = TCPIP_MSG_TIMEOUT;
+ msg->msg.tmo.msecs = msecs;
+ msg->msg.tmo.h = h;
+ msg->msg.tmo.arg = arg;
+ sys_mbox_post(&mbox, msg);
+ return ERR_OK;
+}
+
+/**
+ * call sys_untimeout in tcpip_thread
+ *
+ * @param h function to be called on timeout
+ * @param arg argument to pass to timeout function h
+ * @return ERR_MEM on memory error, ERR_OK otherwise
+ */
+err_t
+tcpip_untimeout(sys_timeout_handler h, void *arg)
+{
+ struct tcpip_msg *msg;
+
+ LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(mbox));
+
+ msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API);
+ if (msg == NULL) {
+ return ERR_MEM;
+ }
+
+ msg->type = TCPIP_MSG_UNTIMEOUT;
+ msg->msg.tmo.h = h;
+ msg->msg.tmo.arg = arg;
+ sys_mbox_post(&mbox, msg);
+ return ERR_OK;
+}
+#endif /* LWIP_TCPIP_TIMEOUT && LWIP_TIMERS */
+
+
+/**
+ * Sends a message to TCPIP thread to call a function. Caller thread blocks on
+ * on a provided semaphore, which ist NOT automatically signalled by TCPIP thread,
+ * this has to be done by the user.
+ * It is recommended to use LWIP_TCPIP_CORE_LOCKING since this is the way
+ * with least runtime overhead.
+ *
+ * @param fn function to be called from TCPIP thread
+ * @param apimsg argument to API function
+ * @param sem semaphore to wait on
+ * @return ERR_OK if the function was called, another err_t if not
+ */
+err_t
+tcpip_send_msg_wait_sem(tcpip_callback_fn fn, void *apimsg, sys_sem_t* sem)
+{
+#if LWIP_TCPIP_CORE_LOCKING
+ LWIP_UNUSED_ARG(sem);
+ LOCK_TCPIP_CORE();
+ fn(apimsg);
+ UNLOCK_TCPIP_CORE();
+ return ERR_OK;
+#else /* LWIP_TCPIP_CORE_LOCKING */
+ TCPIP_MSG_VAR_DECLARE(msg);
+
+ LWIP_ASSERT("semaphore not initialized", sys_sem_valid(sem));
+ LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(mbox));
+
+ TCPIP_MSG_VAR_ALLOC(msg);
+ TCPIP_MSG_VAR_REF(msg).type = TCPIP_MSG_API;
+ TCPIP_MSG_VAR_REF(msg).msg.api_msg.function = fn;
+ TCPIP_MSG_VAR_REF(msg).msg.api_msg.msg = apimsg;
+ sys_mbox_post(&mbox, &TCPIP_MSG_VAR_REF(msg));
+ sys_arch_sem_wait(sem, 0);
+ TCPIP_MSG_VAR_FREE(msg);
+ return ERR_OK;
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+}
+
+/**
+ * Synchronously calls function in TCPIP thread and waits for its completion.
+ * It is recommended to use LWIP_TCPIP_CORE_LOCKING (preferred) or
+ * LWIP_NETCONN_SEM_PER_THREAD.
+ * If not, a semaphore is created and destroyed on every call which is usually
+ * an expensive/slow operation.
+ * @param fn Function to call
+ * @param call Call parameters
+ * @return Return value from tcpip_api_call_fn
+ */
+err_t
+tcpip_api_call(tcpip_api_call_fn fn, struct tcpip_api_call_data *call)
+{
+#if LWIP_TCPIP_CORE_LOCKING
+ err_t err;
+ LOCK_TCPIP_CORE();
+ err = fn(call);
+ UNLOCK_TCPIP_CORE();
+ return err;
+#else /* LWIP_TCPIP_CORE_LOCKING */
+ TCPIP_MSG_VAR_DECLARE(msg);
+
+#if !LWIP_NETCONN_SEM_PER_THREAD
+ err_t err = sys_sem_new(&call->sem, 0);
+ if (err != ERR_OK) {
+ return err;
+ }
+#endif /* LWIP_NETCONN_SEM_PER_THREAD */
+
+ LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(mbox));
+
+ TCPIP_MSG_VAR_ALLOC(msg);
+ TCPIP_MSG_VAR_REF(msg).type = TCPIP_MSG_API_CALL;
+ TCPIP_MSG_VAR_REF(msg).msg.api_call.arg = call;
+ TCPIP_MSG_VAR_REF(msg).msg.api_call.function = fn;
+#if LWIP_NETCONN_SEM_PER_THREAD
+ TCPIP_MSG_VAR_REF(msg).msg.api_call.sem = LWIP_NETCONN_THREAD_SEM_GET();
+#else /* LWIP_NETCONN_SEM_PER_THREAD */
+ TCPIP_MSG_VAR_REF(msg).msg.api_call.sem = &call->sem;
+#endif /* LWIP_NETCONN_SEM_PER_THREAD */
+ sys_mbox_post(&mbox, &TCPIP_MSG_VAR_REF(msg));
+ sys_arch_sem_wait(TCPIP_MSG_VAR_REF(msg).msg.api_call.sem, 0);
+ TCPIP_MSG_VAR_FREE(msg);
+
+#if !LWIP_NETCONN_SEM_PER_THREAD
+ sys_sem_free(&call->sem);
+#endif /* LWIP_NETCONN_SEM_PER_THREAD */
+
+ return call->err;
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+}
+
+/**
+ * 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)
+{
+ LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(mbox));
+ return sys_mbox_trypost(&mbox, msg);
+}
+
+/**
+ * @ingroup lwip_os
+ * Initialize this module:
+ * - initialize all sub modules
+ * - start the tcpip_thread
+ *
+ * @param initfunc a function to call when tcpip_thread is running and finished initializing
+ * @param arg argument to pass to initfunc
+ */
+void
+tcpip_init(tcpip_init_done_fn initfunc, void *arg)
+{
+ lwip_init();
+
+ tcpip_init_done = initfunc;
+ tcpip_init_done_arg = arg;
+ if (sys_mbox_new(&mbox, TCPIP_MBOX_SIZE) != ERR_OK) {
+ LWIP_ASSERT("failed to create tcpip_thread mbox", 0);
+ }
+#if LWIP_TCPIP_CORE_LOCKING
+ if (sys_mutex_new(&lock_tcpip_core) != ERR_OK) {
+ LWIP_ASSERT("failed to create lock_tcpip_core", 0);
+ }
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+
+ sys_thread_new(TCPIP_THREAD_NAME, tcpip_thread, NULL, TCPIP_THREAD_STACKSIZE, TCPIP_THREAD_PRIO);
+}
+
+/**
+ * Simple callback function used with tcpip_callback to free a pbuf
+ * (pbuf_free has a wrong signature for tcpip_callback)
+ *
+ * @param p The pbuf (chain) to be dereferenced.
+ */
+static void
+pbuf_free_int(void *p)
+{
+ struct pbuf *q = (struct pbuf *)p;
+ pbuf_free(q);
+}
+
+/**
+ * A simple wrapper function that allows you to free a pbuf from interrupt context.
+ *
+ * @param p The pbuf (chain) to be dereferenced.
+ * @return ERR_OK if callback could be enqueued, an err_t if not
+ */
+err_t
+pbuf_free_callback(struct pbuf *p)
+{
+ return tcpip_callback_with_block(pbuf_free_int, p, 0);
+}
+
+/**
+ * A simple wrapper function that allows you to free heap memory from
+ * interrupt context.
+ *
+ * @param m the heap memory to free
+ * @return ERR_OK if callback could be enqueued, an err_t if not
+ */
+err_t
+mem_free_callback(void *m)
+{
+ return tcpip_callback_with_block(mem_free, m, 0);
+}
+
+#endif /* !NO_SYS */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/httpd/fs.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/httpd/fs.c
new file mode 100644
index 0000000..35b5e31
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/httpd/fs.c
@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/apps/httpd_opts.h"
+#include "lwip/def.h"
+#include "lwip/apps/fs.h"
+#include "fsdata.h"
+#include <string.h>
+
+
+#if HTTPD_USE_CUSTOM_FSDATA
+#include "fsdata_custom.c"
+#else /* HTTPD_USE_CUSTOM_FSDATA */
+#include "fsdata.c"
+#endif /* HTTPD_USE_CUSTOM_FSDATA */
+
+/*-----------------------------------------------------------------------------------*/
+
+#if LWIP_HTTPD_CUSTOM_FILES
+int fs_open_custom(struct fs_file *file, const char *name);
+void fs_close_custom(struct fs_file *file);
+#if LWIP_HTTPD_FS_ASYNC_READ
+u8_t fs_canread_custom(struct fs_file *file);
+u8_t fs_wait_read_custom(struct fs_file *file, fs_wait_cb callback_fn, void *callback_arg);
+int fs_read_async_custom(struct fs_file *file, char *buffer, int count, fs_wait_cb callback_fn, void *callback_arg);
+#else /* LWIP_HTTPD_FS_ASYNC_READ */
+int fs_read_custom(struct fs_file *file, char *buffer, int count);
+#endif /* LWIP_HTTPD_FS_ASYNC_READ */
+#endif /* LWIP_HTTPD_CUSTOM_FILES */
+
+/*-----------------------------------------------------------------------------------*/
+err_t
+fs_open(struct fs_file *file, const char *name)
+{
+ const struct fsdata_file *f;
+
+ if ((file == NULL) || (name == NULL)) {
+ return ERR_ARG;
+ }
+
+#if LWIP_HTTPD_CUSTOM_FILES
+ if (fs_open_custom(file, name)) {
+ file->is_custom_file = 1;
+ return ERR_OK;
+ }
+ file->is_custom_file = 0;
+#endif /* LWIP_HTTPD_CUSTOM_FILES */
+
+ for (f = FS_ROOT; f != NULL; f = f->next) {
+ if (!strcmp(name, (const char *)f->name)) {
+ file->data = (const char *)f->data;
+ file->len = f->len;
+ file->index = f->len;
+ file->pextension = NULL;
+ file->flags = f->flags;
+#if HTTPD_PRECALCULATED_CHECKSUM
+ file->chksum_count = f->chksum_count;
+ file->chksum = f->chksum;
+#endif /* HTTPD_PRECALCULATED_CHECKSUM */
+#if LWIP_HTTPD_FILE_STATE
+ file->state = fs_state_init(file, name);
+#endif /* #if LWIP_HTTPD_FILE_STATE */
+ return ERR_OK;
+ }
+ }
+ /* file not found */
+ return ERR_VAL;
+}
+
+/*-----------------------------------------------------------------------------------*/
+void
+fs_close(struct fs_file *file)
+{
+#if LWIP_HTTPD_CUSTOM_FILES
+ if (file->is_custom_file) {
+ fs_close_custom(file);
+ }
+#endif /* LWIP_HTTPD_CUSTOM_FILES */
+#if LWIP_HTTPD_FILE_STATE
+ fs_state_free(file, file->state);
+#endif /* #if LWIP_HTTPD_FILE_STATE */
+ LWIP_UNUSED_ARG(file);
+}
+/*-----------------------------------------------------------------------------------*/
+#if LWIP_HTTPD_DYNAMIC_FILE_READ
+#if LWIP_HTTPD_FS_ASYNC_READ
+int
+fs_read_async(struct fs_file *file, char *buffer, int count, fs_wait_cb callback_fn, void *callback_arg)
+#else /* LWIP_HTTPD_FS_ASYNC_READ */
+int
+fs_read(struct fs_file *file, char *buffer, int count)
+#endif /* LWIP_HTTPD_FS_ASYNC_READ */
+{
+ int read;
+ if(file->index == file->len) {
+ return FS_READ_EOF;
+ }
+#if LWIP_HTTPD_FS_ASYNC_READ
+ LWIP_UNUSED_ARG(callback_fn);
+ LWIP_UNUSED_ARG(callback_arg);
+#endif /* LWIP_HTTPD_FS_ASYNC_READ */
+#if LWIP_HTTPD_CUSTOM_FILES
+ if (file->is_custom_file) {
+#if LWIP_HTTPD_FS_ASYNC_READ
+ return fs_read_async_custom(file, buffer, count, callback_fn, callback_arg);
+#else /* LWIP_HTTPD_FS_ASYNC_READ */
+ return fs_read_custom(file, buffer, count);
+#endif /* LWIP_HTTPD_FS_ASYNC_READ */
+ }
+#endif /* LWIP_HTTPD_CUSTOM_FILES */
+
+ read = file->len - file->index;
+ if(read > count) {
+ read = count;
+ }
+
+ MEMCPY(buffer, (file->data + file->index), read);
+ file->index += read;
+
+ return(read);
+}
+#endif /* LWIP_HTTPD_DYNAMIC_FILE_READ */
+/*-----------------------------------------------------------------------------------*/
+#if LWIP_HTTPD_FS_ASYNC_READ
+int
+fs_is_file_ready(struct fs_file *file, fs_wait_cb callback_fn, void *callback_arg)
+{
+ if (file != NULL) {
+#if LWIP_HTTPD_FS_ASYNC_READ
+#if LWIP_HTTPD_CUSTOM_FILES
+ if (!fs_canread_custom(file)) {
+ if (fs_wait_read_custom(file, callback_fn, callback_arg)) {
+ return 0;
+ }
+ }
+#else /* LWIP_HTTPD_CUSTOM_FILES */
+ LWIP_UNUSED_ARG(callback_fn);
+ LWIP_UNUSED_ARG(callback_arg);
+#endif /* LWIP_HTTPD_CUSTOM_FILES */
+#endif /* LWIP_HTTPD_FS_ASYNC_READ */
+ }
+ return 1;
+}
+#endif /* LWIP_HTTPD_FS_ASYNC_READ */
+/*-----------------------------------------------------------------------------------*/
+int
+fs_bytes_left(struct fs_file *file)
+{
+ return file->len - file->index;
+}
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/httpd/fs/404.html b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/httpd/fs/404.html
new file mode 100644
index 0000000..40b343a
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/httpd/fs/404.html
@@ -0,0 +1,21 @@
+<html>
+<head><title>lwIP - A Lightweight TCP/IP Stack</title></head>
+<body bgcolor="white" text="black">
+
+ <table width="100%">
+ <tr valign="top"><td width="80">
+ <a href="http://www.sics.se/"><img src="/img/sics.gif"
+ border="0" alt="SICS logo" title="SICS logo"></a>
+ </td><td width="500">
+ <h1>lwIP - A Lightweight TCP/IP Stack</h1>
+ <h2>404 - Page not found</h2>
+ <p>
+ Sorry, the page you are requesting was not found on this
+ server.
+ </p>
+ </td><td>
+ &nbsp;
+ </td></tr>
+ </table>
+</body>
+</html>
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/httpd/fs/img/sics.gif b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/httpd/fs/img/sics.gif
new file mode 100644
index 0000000..0a4fc7b
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/httpd/fs/img/sics.gif
Binary files differ
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/httpd/fs/index.html b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/httpd/fs/index.html
new file mode 100644
index 0000000..ab575ef
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/httpd/fs/index.html
@@ -0,0 +1,47 @@
+<html>
+<head><title>lwIP - A Lightweight TCP/IP Stack</title></head>
+<body bgcolor="white" text="black">
+
+ <table width="100%">
+ <tr valign="top"><td width="80">
+ <a href="http://www.sics.se/"><img src="/img/sics.gif"
+ border="0" alt="SICS logo" title="SICS logo"></a>
+ </td><td width="500">
+ <h1>lwIP - A Lightweight TCP/IP Stack</h1>
+ <p>
+ The web page you are watching was served by a simple web
+ server running on top of the lightweight TCP/IP stack <a
+ href="http://www.sics.se/~adam/lwip/">lwIP</a>.
+ </p>
+ <p>
+ lwIP is an open source implementation of the TCP/IP
+ protocol suite that was originally written by <a
+ href="http://www.sics.se/~adam/lwip/">Adam Dunkels
+ of the Swedish Institute of Computer Science</a> but now is
+ being actively developed by a team of developers
+ distributed world-wide. Since it's release, lwIP has
+ spurred a lot of interest and has been ported to several
+ platforms and operating systems. lwIP can be used either
+ with or without an underlying OS.
+ </p>
+ <p>
+ The focus of the lwIP TCP/IP implementation is to reduce
+ the RAM usage while still having a full scale TCP. This
+ makes lwIP suitable for use in embedded systems with tens
+ of kilobytes of free RAM and room for around 40 kilobytes
+ of code ROM.
+ </p>
+ <p>
+ More information about lwIP can be found at the lwIP
+ homepage at <a
+ href="http://savannah.nongnu.org/projects/lwip/">http://savannah.nongnu.org/projects/lwip/</a>
+ or at the lwIP wiki at <a
+ href="http://lwip.wikia.com/">http://lwip.wikia.com/</a>.
+ </p>
+ </td><td>
+ &nbsp;
+ </td></tr>
+ </table>
+</body>
+</html>
+
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/httpd/fsdata.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/httpd/fsdata.c
new file mode 100644
index 0000000..6170ce6
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/httpd/fsdata.c
@@ -0,0 +1,298 @@
+#include "lwip/apps/fs.h"
+#include "lwip/def.h"
+#include "fsdata.h"
+
+
+#define file_NULL (struct fsdata_file *) NULL
+
+
+static const unsigned int dummy_align__img_sics_gif = 0;
+static const unsigned char data__img_sics_gif[] = {
+/* /img/sics.gif (14 chars) */
+0x2f,0x69,0x6d,0x67,0x2f,0x73,0x69,0x63,0x73,0x2e,0x67,0x69,0x66,0x00,0x00,0x00,
+
+/* HTTP header */
+/* "HTTP/1.0 200 OK
+" (17 bytes) */
+0x48,0x54,0x54,0x50,0x2f,0x31,0x2e,0x30,0x20,0x32,0x30,0x30,0x20,0x4f,0x4b,0x0d,
+0x0a,
+/* "Server: lwIP/1.3.1 (http://savannah.nongnu.org/projects/lwip)
+" (63 bytes) */
+0x53,0x65,0x72,0x76,0x65,0x72,0x3a,0x20,0x6c,0x77,0x49,0x50,0x2f,0x31,0x2e,0x33,
+0x2e,0x31,0x20,0x28,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x73,0x61,0x76,0x61,0x6e,
+0x6e,0x61,0x68,0x2e,0x6e,0x6f,0x6e,0x67,0x6e,0x75,0x2e,0x6f,0x72,0x67,0x2f,0x70,
+0x72,0x6f,0x6a,0x65,0x63,0x74,0x73,0x2f,0x6c,0x77,0x69,0x70,0x29,0x0d,0x0a,
+/* "Content-type: image/gif
+
+" (27 bytes) */
+0x43,0x6f,0x6e,0x74,0x65,0x6e,0x74,0x2d,0x74,0x79,0x70,0x65,0x3a,0x20,0x69,0x6d,
+0x61,0x67,0x65,0x2f,0x67,0x69,0x66,0x0d,0x0a,0x0d,0x0a,
+/* raw file data (724 bytes) */
+0x47,0x49,0x46,0x38,0x39,0x61,0x46,0x00,0x22,0x00,0xa5,0x00,0x00,0xd9,0x2b,0x39,
+0x6a,0x6a,0x6a,0xbf,0xbf,0xbf,0x93,0x93,0x93,0x0f,0x0f,0x0f,0xb0,0xb0,0xb0,0xa6,
+0xa6,0xa6,0x80,0x80,0x80,0x76,0x76,0x76,0x1e,0x1e,0x1e,0x9d,0x9d,0x9d,0x2e,0x2e,
+0x2e,0x49,0x49,0x49,0x54,0x54,0x54,0x8a,0x8a,0x8a,0x60,0x60,0x60,0xc6,0xa6,0x99,
+0xbd,0xb5,0xb2,0xc2,0xab,0xa1,0xd9,0x41,0x40,0xd5,0x67,0x55,0xc0,0xb0,0xaa,0xd5,
+0x5e,0x4e,0xd6,0x50,0x45,0xcc,0x93,0x7d,0xc8,0xa1,0x90,0xce,0x8b,0x76,0xd2,0x7b,
+0x65,0xd1,0x84,0x6d,0xc9,0x99,0x86,0x3a,0x3a,0x3a,0x00,0x00,0x00,0xb8,0xb8,0xb8,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x2c,0x00,0x00,
+0x00,0x00,0x46,0x00,0x22,0x00,0x00,0x06,0xfe,0x40,0x90,0x70,0x48,0x2c,0x1a,0x8f,
+0xc8,0xa4,0x72,0xc9,0x6c,0x3a,0x9f,0xd0,0xa8,0x74,0x4a,0xad,0x5a,0xaf,0xd8,0xac,
+0x76,0xa9,0x40,0x04,0xbe,0x83,0xe2,0x60,0x3c,0x50,0x20,0x0d,0x8e,0x6f,0x00,0x31,
+0x28,0x1c,0x0d,0x07,0xb5,0xc3,0x60,0x75,0x24,0x3e,0xf8,0xfc,0x87,0x11,0x06,0xe9,
+0x3d,0x46,0x07,0x0b,0x7a,0x7a,0x7c,0x43,0x06,0x1e,0x84,0x78,0x0b,0x07,0x6e,0x51,
+0x01,0x8a,0x84,0x08,0x7e,0x79,0x80,0x87,0x89,0x91,0x7a,0x93,0x0a,0x04,0x99,0x78,
+0x96,0x4f,0x03,0x9e,0x79,0x01,0x94,0x9f,0x43,0x9c,0xa3,0xa4,0x05,0x77,0xa3,0xa0,
+0x4e,0x98,0x79,0x0b,0x1e,0x83,0xa4,0xa6,0x1f,0x96,0x05,0x9d,0xaa,0x78,0x01,0x07,
+0x84,0x04,0x1e,0x1e,0xbb,0xb8,0x51,0x84,0x0e,0x43,0x05,0x07,0x77,0xa5,0x7f,0x42,
+0xb1,0xb2,0x01,0x63,0x08,0x0d,0xbb,0x01,0x0c,0x7a,0x0d,0x44,0x0e,0xd8,0xaf,0x4c,
+0x05,0x7a,0x04,0x47,0x07,0x07,0xb7,0x80,0xa2,0xe1,0x7d,0x44,0x05,0x01,0x04,0x01,
+0xd0,0xea,0x87,0x93,0x4f,0xe0,0x9a,0x49,0xce,0xd8,0x79,0x04,0x66,0x20,0x15,0x10,
+0x10,0x11,0x92,0x29,0x80,0xb6,0xc0,0x91,0x15,0x45,0x1e,0x90,0x19,0x71,0x46,0xa8,
+0x5c,0x04,0x0e,0x00,0x22,0x4e,0xe8,0x40,0x24,0x9f,0x3e,0x04,0x06,0xa7,0x58,0xd4,
+0x93,0xa0,0x1c,0x91,0x3f,0xe8,0xf0,0x88,0x03,0xb1,0x21,0xa2,0x49,0x00,0x19,0x86,
+0xfc,0x52,0x44,0xe0,0x01,0x9d,0x29,0x21,0x15,0x25,0x50,0xf7,0x67,0x25,0x1e,0x06,
+0xfd,0x4e,0x9a,0xb4,0x90,0xac,0x15,0xfa,0xcb,0x52,0x53,0x1e,0x8c,0xf2,0xf8,0x07,
+0x92,0x2d,0x08,0x3a,0x4d,0x12,0x49,0x95,0x49,0xdb,0x14,0x04,0xc4,0x14,0x85,0x29,
+0xaa,0xe7,0x01,0x08,0xa4,0x49,0x01,0x14,0x51,0xe0,0x53,0x91,0xd5,0x29,0x06,0x1a,
+0x64,0x02,0xf4,0xc7,0x81,0x9e,0x05,0x20,0x22,0x64,0xa5,0x30,0xae,0xab,0x9e,0x97,
+0x53,0xd8,0xb9,0xfd,0x50,0xef,0x93,0x02,0x42,0x74,0x34,0xe8,0x9c,0x20,0x21,0xc9,
+0x01,0x68,0x78,0xe6,0x55,0x29,0x20,0x56,0x4f,0x4c,0x40,0x51,0x71,0x82,0xc0,0x70,
+0x21,0x22,0x85,0xbe,0x4b,0x1c,0x44,0x05,0xea,0xa4,0x01,0xbf,0x22,0xb5,0xf0,0x1c,
+0x06,0x51,0x38,0x8f,0xe0,0x22,0xec,0x18,0xac,0x39,0x22,0xd4,0xd6,0x93,0x44,0x01,
+0x32,0x82,0xc8,0xfc,0x61,0xb3,0x01,0x45,0x0c,0x2e,0x83,0x30,0xd0,0x0e,0x17,0x24,
+0x0f,0x70,0x85,0x94,0xee,0x05,0x05,0x53,0x4b,0x32,0x1b,0x3f,0x98,0xd3,0x1d,0x29,
+0x81,0xb0,0xae,0x1e,0x8c,0x7e,0x68,0xe0,0x60,0x5a,0x54,0x8f,0xb0,0x78,0x69,0x73,
+0x06,0xa2,0x00,0x6b,0x57,0xca,0x3d,0x11,0x50,0xbd,0x04,0x30,0x4b,0x3a,0xd4,0xab,
+0x5f,0x1f,0x9b,0x3d,0x13,0x74,0x27,0x88,0x3c,0x25,0xe0,0x17,0xbe,0x7a,0x79,0x45,
+0x0d,0x0c,0xb0,0x8b,0xda,0x90,0xca,0x80,0x06,0x5d,0x17,0x60,0x1c,0x22,0x4c,0xd8,
+0x57,0x22,0x06,0x20,0x00,0x98,0x07,0x08,0xe4,0x56,0x80,0x80,0x1c,0xc5,0xb7,0xc5,
+0x82,0x0c,0x36,0xe8,0xe0,0x83,0x10,0x46,0x28,0xe1,0x84,0x14,0x56,0x68,0xa1,0x10,
+0x41,0x00,0x00,0x3b,};
+
+static const unsigned int dummy_align__404_html = 1;
+static const unsigned char data__404_html[] = {
+/* /404.html (10 chars) */
+0x2f,0x34,0x30,0x34,0x2e,0x68,0x74,0x6d,0x6c,0x00,0x00,0x00,
+
+/* HTTP header */
+/* "HTTP/1.0 404 File not found
+" (29 bytes) */
+0x48,0x54,0x54,0x50,0x2f,0x31,0x2e,0x30,0x20,0x34,0x30,0x34,0x20,0x46,0x69,0x6c,
+0x65,0x20,0x6e,0x6f,0x74,0x20,0x66,0x6f,0x75,0x6e,0x64,0x0d,0x0a,
+/* "Server: lwIP/1.3.1 (http://savannah.nongnu.org/projects/lwip)
+" (63 bytes) */
+0x53,0x65,0x72,0x76,0x65,0x72,0x3a,0x20,0x6c,0x77,0x49,0x50,0x2f,0x31,0x2e,0x33,
+0x2e,0x31,0x20,0x28,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x73,0x61,0x76,0x61,0x6e,
+0x6e,0x61,0x68,0x2e,0x6e,0x6f,0x6e,0x67,0x6e,0x75,0x2e,0x6f,0x72,0x67,0x2f,0x70,
+0x72,0x6f,0x6a,0x65,0x63,0x74,0x73,0x2f,0x6c,0x77,0x69,0x70,0x29,0x0d,0x0a,
+/* "Content-type: text/html
+
+" (27 bytes) */
+0x43,0x6f,0x6e,0x74,0x65,0x6e,0x74,0x2d,0x74,0x79,0x70,0x65,0x3a,0x20,0x74,0x65,
+0x78,0x74,0x2f,0x68,0x74,0x6d,0x6c,0x0d,0x0a,0x0d,0x0a,
+/* raw file data (565 bytes) */
+0x3c,0x68,0x74,0x6d,0x6c,0x3e,0x0d,0x0a,0x3c,0x68,0x65,0x61,0x64,0x3e,0x3c,0x74,
+0x69,0x74,0x6c,0x65,0x3e,0x6c,0x77,0x49,0x50,0x20,0x2d,0x20,0x41,0x20,0x4c,0x69,
+0x67,0x68,0x74,0x77,0x65,0x69,0x67,0x68,0x74,0x20,0x54,0x43,0x50,0x2f,0x49,0x50,
+0x20,0x53,0x74,0x61,0x63,0x6b,0x3c,0x2f,0x74,0x69,0x74,0x6c,0x65,0x3e,0x3c,0x2f,
+0x68,0x65,0x61,0x64,0x3e,0x0d,0x0a,0x3c,0x62,0x6f,0x64,0x79,0x20,0x62,0x67,0x63,
+0x6f,0x6c,0x6f,0x72,0x3d,0x22,0x77,0x68,0x69,0x74,0x65,0x22,0x20,0x74,0x65,0x78,
+0x74,0x3d,0x22,0x62,0x6c,0x61,0x63,0x6b,0x22,0x3e,0x0d,0x0a,0x0d,0x0a,0x20,0x20,
+0x20,0x20,0x3c,0x74,0x61,0x62,0x6c,0x65,0x20,0x77,0x69,0x64,0x74,0x68,0x3d,0x22,
+0x31,0x30,0x30,0x25,0x22,0x3e,0x0d,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x3c,0x74,
+0x72,0x20,0x76,0x61,0x6c,0x69,0x67,0x6e,0x3d,0x22,0x74,0x6f,0x70,0x22,0x3e,0x3c,
+0x74,0x64,0x20,0x77,0x69,0x64,0x74,0x68,0x3d,0x22,0x38,0x30,0x22,0x3e,0x09,0x20,
+0x20,0x0d,0x0a,0x09,0x20,0x20,0x3c,0x61,0x20,0x68,0x72,0x65,0x66,0x3d,0x22,0x68,
+0x74,0x74,0x70,0x3a,0x2f,0x2f,0x77,0x77,0x77,0x2e,0x73,0x69,0x63,0x73,0x2e,0x73,
+0x65,0x2f,0x22,0x3e,0x3c,0x69,0x6d,0x67,0x20,0x73,0x72,0x63,0x3d,0x22,0x2f,0x69,
+0x6d,0x67,0x2f,0x73,0x69,0x63,0x73,0x2e,0x67,0x69,0x66,0x22,0x0d,0x0a,0x09,0x20,
+0x20,0x62,0x6f,0x72,0x64,0x65,0x72,0x3d,0x22,0x30,0x22,0x20,0x61,0x6c,0x74,0x3d,
+0x22,0x53,0x49,0x43,0x53,0x20,0x6c,0x6f,0x67,0x6f,0x22,0x20,0x74,0x69,0x74,0x6c,
+0x65,0x3d,0x22,0x53,0x49,0x43,0x53,0x20,0x6c,0x6f,0x67,0x6f,0x22,0x3e,0x3c,0x2f,
+0x61,0x3e,0x0d,0x0a,0x09,0x3c,0x2f,0x74,0x64,0x3e,0x3c,0x74,0x64,0x20,0x77,0x69,
+0x64,0x74,0x68,0x3d,0x22,0x35,0x30,0x30,0x22,0x3e,0x09,0x20,0x20,0x0d,0x0a,0x09,
+0x20,0x20,0x3c,0x68,0x31,0x3e,0x6c,0x77,0x49,0x50,0x20,0x2d,0x20,0x41,0x20,0x4c,
+0x69,0x67,0x68,0x74,0x77,0x65,0x69,0x67,0x68,0x74,0x20,0x54,0x43,0x50,0x2f,0x49,
+0x50,0x20,0x53,0x74,0x61,0x63,0x6b,0x3c,0x2f,0x68,0x31,0x3e,0x0d,0x0a,0x09,0x20,
+0x20,0x3c,0x68,0x32,0x3e,0x34,0x30,0x34,0x20,0x2d,0x20,0x50,0x61,0x67,0x65,0x20,
+0x6e,0x6f,0x74,0x20,0x66,0x6f,0x75,0x6e,0x64,0x3c,0x2f,0x68,0x32,0x3e,0x0d,0x0a,
+0x09,0x20,0x20,0x3c,0x70,0x3e,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,0x53,0x6f,0x72,
+0x72,0x79,0x2c,0x20,0x74,0x68,0x65,0x20,0x70,0x61,0x67,0x65,0x20,0x79,0x6f,0x75,
+0x20,0x61,0x72,0x65,0x20,0x72,0x65,0x71,0x75,0x65,0x73,0x74,0x69,0x6e,0x67,0x20,
+0x77,0x61,0x73,0x20,0x6e,0x6f,0x74,0x20,0x66,0x6f,0x75,0x6e,0x64,0x20,0x6f,0x6e,
+0x20,0x74,0x68,0x69,0x73,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,0x73,0x65,0x72,0x76,
+0x65,0x72,0x2e,0x20,0x0d,0x0a,0x09,0x20,0x20,0x3c,0x2f,0x70,0x3e,0x0d,0x0a,0x09,
+0x3c,0x2f,0x74,0x64,0x3e,0x3c,0x74,0x64,0x3e,0x0d,0x0a,0x09,0x20,0x20,0x26,0x6e,
+0x62,0x73,0x70,0x3b,0x0d,0x0a,0x09,0x3c,0x2f,0x74,0x64,0x3e,0x3c,0x2f,0x74,0x72,
+0x3e,0x0d,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x3c,0x2f,0x74,0x61,0x62,0x6c,0x65,
+0x3e,0x0d,0x0a,0x3c,0x2f,0x62,0x6f,0x64,0x79,0x3e,0x0d,0x0a,0x3c,0x2f,0x68,0x74,
+0x6d,0x6c,0x3e,0x0d,0x0a,};
+
+static const unsigned int dummy_align__index_html = 2;
+static const unsigned char data__index_html[] = {
+/* /index.html (12 chars) */
+0x2f,0x69,0x6e,0x64,0x65,0x78,0x2e,0x68,0x74,0x6d,0x6c,0x00,
+
+/* HTTP header */
+/* "HTTP/1.0 200 OK
+" (17 bytes) */
+0x48,0x54,0x54,0x50,0x2f,0x31,0x2e,0x30,0x20,0x32,0x30,0x30,0x20,0x4f,0x4b,0x0d,
+0x0a,
+/* "Server: lwIP/1.3.1 (http://savannah.nongnu.org/projects/lwip)
+" (63 bytes) */
+0x53,0x65,0x72,0x76,0x65,0x72,0x3a,0x20,0x6c,0x77,0x49,0x50,0x2f,0x31,0x2e,0x33,
+0x2e,0x31,0x20,0x28,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x73,0x61,0x76,0x61,0x6e,
+0x6e,0x61,0x68,0x2e,0x6e,0x6f,0x6e,0x67,0x6e,0x75,0x2e,0x6f,0x72,0x67,0x2f,0x70,
+0x72,0x6f,0x6a,0x65,0x63,0x74,0x73,0x2f,0x6c,0x77,0x69,0x70,0x29,0x0d,0x0a,
+/* "Content-type: text/html
+
+" (27 bytes) */
+0x43,0x6f,0x6e,0x74,0x65,0x6e,0x74,0x2d,0x74,0x79,0x70,0x65,0x3a,0x20,0x74,0x65,
+0x78,0x74,0x2f,0x68,0x74,0x6d,0x6c,0x0d,0x0a,0x0d,0x0a,
+/* raw file data (1751 bytes) */
+0x3c,0x68,0x74,0x6d,0x6c,0x3e,0x0d,0x0a,0x3c,0x68,0x65,0x61,0x64,0x3e,0x3c,0x74,
+0x69,0x74,0x6c,0x65,0x3e,0x6c,0x77,0x49,0x50,0x20,0x2d,0x20,0x41,0x20,0x4c,0x69,
+0x67,0x68,0x74,0x77,0x65,0x69,0x67,0x68,0x74,0x20,0x54,0x43,0x50,0x2f,0x49,0x50,
+0x20,0x53,0x74,0x61,0x63,0x6b,0x3c,0x2f,0x74,0x69,0x74,0x6c,0x65,0x3e,0x3c,0x2f,
+0x68,0x65,0x61,0x64,0x3e,0x0d,0x0a,0x3c,0x62,0x6f,0x64,0x79,0x20,0x62,0x67,0x63,
+0x6f,0x6c,0x6f,0x72,0x3d,0x22,0x77,0x68,0x69,0x74,0x65,0x22,0x20,0x74,0x65,0x78,
+0x74,0x3d,0x22,0x62,0x6c,0x61,0x63,0x6b,0x22,0x3e,0x0d,0x0a,0x0d,0x0a,0x20,0x20,
+0x20,0x20,0x3c,0x74,0x61,0x62,0x6c,0x65,0x20,0x77,0x69,0x64,0x74,0x68,0x3d,0x22,
+0x31,0x30,0x30,0x25,0x22,0x3e,0x0d,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x3c,0x74,
+0x72,0x20,0x76,0x61,0x6c,0x69,0x67,0x6e,0x3d,0x22,0x74,0x6f,0x70,0x22,0x3e,0x3c,
+0x74,0x64,0x20,0x77,0x69,0x64,0x74,0x68,0x3d,0x22,0x38,0x30,0x22,0x3e,0x09,0x20,
+0x20,0x0d,0x0a,0x09,0x20,0x20,0x3c,0x61,0x20,0x68,0x72,0x65,0x66,0x3d,0x22,0x68,
+0x74,0x74,0x70,0x3a,0x2f,0x2f,0x77,0x77,0x77,0x2e,0x73,0x69,0x63,0x73,0x2e,0x73,
+0x65,0x2f,0x22,0x3e,0x3c,0x69,0x6d,0x67,0x20,0x73,0x72,0x63,0x3d,0x22,0x2f,0x69,
+0x6d,0x67,0x2f,0x73,0x69,0x63,0x73,0x2e,0x67,0x69,0x66,0x22,0x0d,0x0a,0x09,0x20,
+0x20,0x62,0x6f,0x72,0x64,0x65,0x72,0x3d,0x22,0x30,0x22,0x20,0x61,0x6c,0x74,0x3d,
+0x22,0x53,0x49,0x43,0x53,0x20,0x6c,0x6f,0x67,0x6f,0x22,0x20,0x74,0x69,0x74,0x6c,
+0x65,0x3d,0x22,0x53,0x49,0x43,0x53,0x20,0x6c,0x6f,0x67,0x6f,0x22,0x3e,0x3c,0x2f,
+0x61,0x3e,0x0d,0x0a,0x09,0x3c,0x2f,0x74,0x64,0x3e,0x3c,0x74,0x64,0x20,0x77,0x69,
+0x64,0x74,0x68,0x3d,0x22,0x35,0x30,0x30,0x22,0x3e,0x09,0x20,0x20,0x0d,0x0a,0x09,
+0x20,0x20,0x3c,0x68,0x31,0x3e,0x6c,0x77,0x49,0x50,0x20,0x2d,0x20,0x41,0x20,0x4c,
+0x69,0x67,0x68,0x74,0x77,0x65,0x69,0x67,0x68,0x74,0x20,0x54,0x43,0x50,0x2f,0x49,
+0x50,0x20,0x53,0x74,0x61,0x63,0x6b,0x3c,0x2f,0x68,0x31,0x3e,0x0d,0x0a,0x09,0x20,
+0x20,0x3c,0x70,0x3e,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,0x54,0x68,0x65,0x20,0x77,
+0x65,0x62,0x20,0x70,0x61,0x67,0x65,0x20,0x79,0x6f,0x75,0x20,0x61,0x72,0x65,0x20,
+0x77,0x61,0x74,0x63,0x68,0x69,0x6e,0x67,0x20,0x77,0x61,0x73,0x20,0x73,0x65,0x72,
+0x76,0x65,0x64,0x20,0x62,0x79,0x20,0x61,0x20,0x73,0x69,0x6d,0x70,0x6c,0x65,0x20,
+0x77,0x65,0x62,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,0x73,0x65,0x72,0x76,0x65,0x72,
+0x20,0x72,0x75,0x6e,0x6e,0x69,0x6e,0x67,0x20,0x6f,0x6e,0x20,0x74,0x6f,0x70,0x20,
+0x6f,0x66,0x20,0x74,0x68,0x65,0x20,0x6c,0x69,0x67,0x68,0x74,0x77,0x65,0x69,0x67,
+0x68,0x74,0x20,0x54,0x43,0x50,0x2f,0x49,0x50,0x20,0x73,0x74,0x61,0x63,0x6b,0x20,
+0x3c,0x61,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,0x68,0x72,0x65,0x66,0x3d,0x22,0x68,
+0x74,0x74,0x70,0x3a,0x2f,0x2f,0x77,0x77,0x77,0x2e,0x73,0x69,0x63,0x73,0x2e,0x73,
+0x65,0x2f,0x7e,0x61,0x64,0x61,0x6d,0x2f,0x6c,0x77,0x69,0x70,0x2f,0x22,0x3e,0x6c,
+0x77,0x49,0x50,0x3c,0x2f,0x61,0x3e,0x2e,0x0d,0x0a,0x09,0x20,0x20,0x3c,0x2f,0x70,
+0x3e,0x0d,0x0a,0x09,0x20,0x20,0x3c,0x70,0x3e,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,
+0x6c,0x77,0x49,0x50,0x20,0x69,0x73,0x20,0x61,0x6e,0x20,0x6f,0x70,0x65,0x6e,0x20,
+0x73,0x6f,0x75,0x72,0x63,0x65,0x20,0x69,0x6d,0x70,0x6c,0x65,0x6d,0x65,0x6e,0x74,
+0x61,0x74,0x69,0x6f,0x6e,0x20,0x6f,0x66,0x20,0x74,0x68,0x65,0x20,0x54,0x43,0x50,
+0x2f,0x49,0x50,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,0x70,0x72,0x6f,0x74,0x6f,0x63,
+0x6f,0x6c,0x20,0x73,0x75,0x69,0x74,0x65,0x20,0x74,0x68,0x61,0x74,0x20,0x77,0x61,
+0x73,0x20,0x6f,0x72,0x69,0x67,0x69,0x6e,0x61,0x6c,0x6c,0x79,0x20,0x77,0x72,0x69,
+0x74,0x74,0x65,0x6e,0x20,0x62,0x79,0x20,0x3c,0x61,0x0d,0x0a,0x09,0x20,0x20,0x20,
+0x20,0x68,0x72,0x65,0x66,0x3d,0x22,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x77,0x77,
+0x77,0x2e,0x73,0x69,0x63,0x73,0x2e,0x73,0x65,0x2f,0x7e,0x61,0x64,0x61,0x6d,0x2f,
+0x6c,0x77,0x69,0x70,0x2f,0x22,0x3e,0x41,0x64,0x61,0x6d,0x20,0x44,0x75,0x6e,0x6b,
+0x65,0x6c,0x73,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,0x6f,0x66,0x20,0x74,0x68,0x65,
+0x20,0x53,0x77,0x65,0x64,0x69,0x73,0x68,0x20,0x49,0x6e,0x73,0x74,0x69,0x74,0x75,
+0x74,0x65,0x20,0x6f,0x66,0x20,0x43,0x6f,0x6d,0x70,0x75,0x74,0x65,0x72,0x20,0x53,
+0x63,0x69,0x65,0x6e,0x63,0x65,0x3c,0x2f,0x61,0x3e,0x20,0x62,0x75,0x74,0x20,0x6e,
+0x6f,0x77,0x20,0x69,0x73,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,0x62,0x65,0x69,0x6e,
+0x67,0x20,0x61,0x63,0x74,0x69,0x76,0x65,0x6c,0x79,0x20,0x64,0x65,0x76,0x65,0x6c,
+0x6f,0x70,0x65,0x64,0x20,0x62,0x79,0x20,0x61,0x20,0x74,0x65,0x61,0x6d,0x20,0x6f,
+0x66,0x20,0x64,0x65,0x76,0x65,0x6c,0x6f,0x70,0x65,0x72,0x73,0x0d,0x0a,0x09,0x20,
+0x20,0x20,0x20,0x64,0x69,0x73,0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x64,0x20,0x77,
+0x6f,0x72,0x6c,0x64,0x2d,0x77,0x69,0x64,0x65,0x2e,0x20,0x53,0x69,0x6e,0x63,0x65,
+0x20,0x69,0x74,0x27,0x73,0x20,0x72,0x65,0x6c,0x65,0x61,0x73,0x65,0x2c,0x20,0x6c,
+0x77,0x49,0x50,0x20,0x68,0x61,0x73,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,0x73,0x70,
+0x75,0x72,0x72,0x65,0x64,0x20,0x61,0x20,0x6c,0x6f,0x74,0x20,0x6f,0x66,0x20,0x69,
+0x6e,0x74,0x65,0x72,0x65,0x73,0x74,0x20,0x61,0x6e,0x64,0x20,0x68,0x61,0x73,0x20,
+0x62,0x65,0x65,0x6e,0x20,0x70,0x6f,0x72,0x74,0x65,0x64,0x20,0x74,0x6f,0x20,0x73,
+0x65,0x76,0x65,0x72,0x61,0x6c,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,0x70,0x6c,0x61,
+0x74,0x66,0x6f,0x72,0x6d,0x73,0x20,0x61,0x6e,0x64,0x20,0x6f,0x70,0x65,0x72,0x61,
+0x74,0x69,0x6e,0x67,0x20,0x73,0x79,0x73,0x74,0x65,0x6d,0x73,0x2e,0x20,0x6c,0x77,
+0x49,0x50,0x20,0x63,0x61,0x6e,0x20,0x62,0x65,0x20,0x75,0x73,0x65,0x64,0x20,0x65,
+0x69,0x74,0x68,0x65,0x72,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,0x77,0x69,0x74,0x68,
+0x20,0x6f,0x72,0x20,0x77,0x69,0x74,0x68,0x6f,0x75,0x74,0x20,0x61,0x6e,0x20,0x75,
+0x6e,0x64,0x65,0x72,0x6c,0x79,0x69,0x6e,0x67,0x20,0x4f,0x53,0x2e,0x0d,0x0a,0x09,
+0x20,0x20,0x3c,0x2f,0x70,0x3e,0x0d,0x0a,0x09,0x20,0x20,0x3c,0x70,0x3e,0x0d,0x0a,
+0x09,0x20,0x20,0x20,0x20,0x54,0x68,0x65,0x20,0x66,0x6f,0x63,0x75,0x73,0x20,0x6f,
+0x66,0x20,0x74,0x68,0x65,0x20,0x6c,0x77,0x49,0x50,0x20,0x54,0x43,0x50,0x2f,0x49,
+0x50,0x20,0x69,0x6d,0x70,0x6c,0x65,0x6d,0x65,0x6e,0x74,0x61,0x74,0x69,0x6f,0x6e,
+0x20,0x69,0x73,0x20,0x74,0x6f,0x20,0x72,0x65,0x64,0x75,0x63,0x65,0x0d,0x0a,0x09,
+0x20,0x20,0x20,0x20,0x74,0x68,0x65,0x20,0x52,0x41,0x4d,0x20,0x75,0x73,0x61,0x67,
+0x65,0x20,0x77,0x68,0x69,0x6c,0x65,0x20,0x73,0x74,0x69,0x6c,0x6c,0x20,0x68,0x61,
+0x76,0x69,0x6e,0x67,0x20,0x61,0x20,0x66,0x75,0x6c,0x6c,0x20,0x73,0x63,0x61,0x6c,
+0x65,0x20,0x54,0x43,0x50,0x2e,0x20,0x54,0x68,0x69,0x73,0x0d,0x0a,0x09,0x20,0x20,
+0x20,0x20,0x6d,0x61,0x6b,0x65,0x73,0x20,0x6c,0x77,0x49,0x50,0x20,0x73,0x75,0x69,
+0x74,0x61,0x62,0x6c,0x65,0x20,0x66,0x6f,0x72,0x20,0x75,0x73,0x65,0x20,0x69,0x6e,
+0x20,0x65,0x6d,0x62,0x65,0x64,0x64,0x65,0x64,0x20,0x73,0x79,0x73,0x74,0x65,0x6d,
+0x73,0x20,0x77,0x69,0x74,0x68,0x20,0x74,0x65,0x6e,0x73,0x0d,0x0a,0x09,0x20,0x20,
+0x20,0x20,0x6f,0x66,0x20,0x6b,0x69,0x6c,0x6f,0x62,0x79,0x74,0x65,0x73,0x20,0x6f,
+0x66,0x20,0x66,0x72,0x65,0x65,0x20,0x52,0x41,0x4d,0x20,0x61,0x6e,0x64,0x20,0x72,
+0x6f,0x6f,0x6d,0x20,0x66,0x6f,0x72,0x20,0x61,0x72,0x6f,0x75,0x6e,0x64,0x20,0x34,
+0x30,0x20,0x6b,0x69,0x6c,0x6f,0x62,0x79,0x74,0x65,0x73,0x0d,0x0a,0x09,0x20,0x20,
+0x20,0x20,0x6f,0x66,0x20,0x63,0x6f,0x64,0x65,0x20,0x52,0x4f,0x4d,0x2e,0x0d,0x0a,
+0x09,0x20,0x20,0x3c,0x2f,0x70,0x3e,0x0d,0x0a,0x09,0x20,0x20,0x3c,0x70,0x3e,0x0d,
+0x0a,0x09,0x20,0x20,0x20,0x20,0x4d,0x6f,0x72,0x65,0x20,0x69,0x6e,0x66,0x6f,0x72,
+0x6d,0x61,0x74,0x69,0x6f,0x6e,0x20,0x61,0x62,0x6f,0x75,0x74,0x20,0x6c,0x77,0x49,
+0x50,0x20,0x63,0x61,0x6e,0x20,0x62,0x65,0x20,0x66,0x6f,0x75,0x6e,0x64,0x20,0x61,
+0x74,0x20,0x74,0x68,0x65,0x20,0x6c,0x77,0x49,0x50,0x0d,0x0a,0x09,0x20,0x20,0x20,
+0x20,0x68,0x6f,0x6d,0x65,0x70,0x61,0x67,0x65,0x20,0x61,0x74,0x20,0x3c,0x61,0x0d,
+0x0a,0x09,0x20,0x20,0x20,0x20,0x68,0x72,0x65,0x66,0x3d,0x22,0x68,0x74,0x74,0x70,
+0x3a,0x2f,0x2f,0x73,0x61,0x76,0x61,0x6e,0x6e,0x61,0x68,0x2e,0x6e,0x6f,0x6e,0x67,
+0x6e,0x75,0x2e,0x6f,0x72,0x67,0x2f,0x70,0x72,0x6f,0x6a,0x65,0x63,0x74,0x73,0x2f,
+0x6c,0x77,0x69,0x70,0x2f,0x22,0x3e,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x73,0x61,
+0x76,0x61,0x6e,0x6e,0x61,0x68,0x2e,0x6e,0x6f,0x6e,0x67,0x6e,0x75,0x2e,0x6f,0x72,
+0x67,0x2f,0x70,0x72,0x6f,0x6a,0x65,0x63,0x74,0x73,0x2f,0x6c,0x77,0x69,0x70,0x2f,
+0x3c,0x2f,0x61,0x3e,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,0x6f,0x72,0x20,0x61,0x74,
+0x20,0x74,0x68,0x65,0x20,0x6c,0x77,0x49,0x50,0x20,0x77,0x69,0x6b,0x69,0x20,0x61,
+0x74,0x20,0x3c,0x61,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,0x68,0x72,0x65,0x66,0x3d,
+0x22,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x6c,0x77,0x69,0x70,0x2e,0x77,0x69,0x6b,
+0x69,0x61,0x2e,0x63,0x6f,0x6d,0x2f,0x22,0x3e,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,
+0x6c,0x77,0x69,0x70,0x2e,0x77,0x69,0x6b,0x69,0x61,0x2e,0x63,0x6f,0x6d,0x2f,0x3c,
+0x2f,0x61,0x3e,0x2e,0x0d,0x0a,0x09,0x20,0x20,0x3c,0x2f,0x70,0x3e,0x0d,0x0a,0x09,
+0x3c,0x2f,0x74,0x64,0x3e,0x3c,0x74,0x64,0x3e,0x0d,0x0a,0x09,0x20,0x20,0x26,0x6e,
+0x62,0x73,0x70,0x3b,0x0d,0x0a,0x09,0x3c,0x2f,0x74,0x64,0x3e,0x3c,0x2f,0x74,0x72,
+0x3e,0x0d,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x3c,0x2f,0x74,0x61,0x62,0x6c,0x65,
+0x3e,0x0d,0x0a,0x3c,0x2f,0x62,0x6f,0x64,0x79,0x3e,0x0d,0x0a,0x3c,0x2f,0x68,0x74,
+0x6d,0x6c,0x3e,0x0d,0x0a,0x0d,0x0a,};
+
+
+
+const struct fsdata_file file__img_sics_gif[] = { {
+file_NULL,
+data__img_sics_gif,
+data__img_sics_gif + 16,
+sizeof(data__img_sics_gif) - 16,
+1,
+}};
+
+const struct fsdata_file file__404_html[] = { {
+file__img_sics_gif,
+data__404_html,
+data__404_html + 12,
+sizeof(data__404_html) - 12,
+1,
+}};
+
+const struct fsdata_file file__index_html[] = { {
+file__404_html,
+data__index_html,
+data__index_html + 12,
+sizeof(data__index_html) - 12,
+1,
+}};
+
+#define FS_ROOT file__index_html
+#define FS_NUMFILES 3
+
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/httpd/fsdata.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/httpd/fsdata.h
new file mode 100644
index 0000000..ac4548c
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/httpd/fsdata.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: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef LWIP_FSDATA_H
+#define LWIP_FSDATA_H
+
+#include "lwip/apps/httpd_opts.h"
+#include "lwip/apps/fs.h"
+
+struct fsdata_file {
+ const struct fsdata_file *next;
+ const unsigned char *name;
+ const unsigned char *data;
+ int len;
+ u8_t flags;
+#if HTTPD_PRECALCULATED_CHECKSUM
+ u16_t chksum_count;
+ const struct fsdata_chksum *chksum;
+#endif /* HTTPD_PRECALCULATED_CHECKSUM */
+};
+
+#endif /* LWIP_FSDATA_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/httpd/httpd.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/httpd/httpd.c
new file mode 100644
index 0000000..43195d7
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/httpd/httpd.c
@@ -0,0 +1,2629 @@
+/**
+ * @file
+ * LWIP HTTP server implementation
+ */
+
+/*
+ * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ * Simon Goldschmidt
+ *
+ */
+
+/**
+ * @defgroup httpd HTTP server
+ * @ingroup apps
+ *
+ * This httpd supports for a
+ * rudimentary server-side-include facility which will replace tags of the form
+ * <!--#tag--> in any file whose extension is .shtml, .shtm or .ssi with
+ * strings provided by an include handler whose pointer is provided to the
+ * module via function http_set_ssi_handler().
+ * Additionally, a simple common
+ * gateway interface (CGI) handling mechanism has been added to allow clients
+ * to hook functions to particular request URIs.
+ *
+ * To enable SSI support, define label LWIP_HTTPD_SSI in lwipopts.h.
+ * To enable CGI support, define label LWIP_HTTPD_CGI in lwipopts.h.
+ *
+ * By default, the server assumes that HTTP headers are already present in
+ * each file stored in the file system. By defining LWIP_HTTPD_DYNAMIC_HEADERS in
+ * lwipopts.h, this behavior can be changed such that the server inserts the
+ * headers automatically based on the extension of the file being served. If
+ * this mode is used, be careful to ensure that the file system image used
+ * does not already contain the header information.
+ *
+ * File system images without headers can be created using the makefsfile
+ * tool with the -h command line option.
+ *
+ *
+ * Notes about valid SSI tags
+ * --------------------------
+ *
+ * The following assumptions are made about tags used in SSI markers:
+ *
+ * 1. No tag may contain '-' or whitespace characters within the tag name.
+ * 2. Whitespace is allowed between the tag leadin "<!--#" and the start of
+ * the tag name and between the tag name and the leadout string "-->".
+ * 3. The maximum tag name length is LWIP_HTTPD_MAX_TAG_NAME_LEN, currently 8 characters.
+ *
+ * Notes on CGI usage
+ * ------------------
+ *
+ * The simple CGI support offered here works with GET method requests only
+ * and can handle up to 16 parameters encoded into the URI. The handler
+ * function may not write directly to the HTTP output but must return a
+ * filename that the HTTP server will send to the browser as a response to
+ * the incoming CGI request.
+ *
+ *
+ *
+ * The list of supported file types is quite short, so if makefsdata complains
+ * about an unknown extension, make sure to add it (and its doctype) to
+ * the 'g_psHTTPHeaders' list.
+ */
+#include "lwip/init.h"
+#include "lwip/apps/httpd.h"
+#include "lwip/debug.h"
+#include "lwip/stats.h"
+#include "lwip/apps/fs.h"
+#include "httpd_structs.h"
+#include "lwip/def.h"
+#include "lwip/ip.h"
+#include "lwip/tcp.h"
+
+#include <string.h> /* memset */
+#include <stdlib.h> /* atoi */
+#include <stdio.h>
+
+#if LWIP_TCP && LWIP_CALLBACK_API
+
+/** Minimum length for a valid HTTP/0.9 request: "GET /\r\n" -> 7 bytes */
+#define MIN_REQ_LEN 7
+
+#define CRLF "\r\n"
+#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE
+#define HTTP11_CONNECTIONKEEPALIVE "Connection: keep-alive"
+#define HTTP11_CONNECTIONKEEPALIVE2 "Connection: Keep-Alive"
+#endif
+
+/** These defines check whether tcp_write has to copy data or not */
+
+/** This was TI's check whether to let TCP copy data or not
+ * \#define HTTP_IS_DATA_VOLATILE(hs) ((hs->file < (char *)0x20000000) ? 0 : TCP_WRITE_FLAG_COPY)
+ */
+#ifndef HTTP_IS_DATA_VOLATILE
+#if LWIP_HTTPD_SSI
+/* Copy for SSI files, no copy for non-SSI files */
+#define HTTP_IS_DATA_VOLATILE(hs) ((hs)->ssi ? TCP_WRITE_FLAG_COPY : 0)
+#else /* LWIP_HTTPD_SSI */
+/** Default: don't copy if the data is sent from file-system directly */
+#define HTTP_IS_DATA_VOLATILE(hs) (((hs->file != NULL) && (hs->handle != NULL) && (hs->file == \
+ (const char*)hs->handle->data + hs->handle->len - hs->left)) \
+ ? 0 : TCP_WRITE_FLAG_COPY)
+#endif /* LWIP_HTTPD_SSI */
+#endif
+
+/** Default: headers are sent from ROM */
+#ifndef HTTP_IS_HDR_VOLATILE
+#define HTTP_IS_HDR_VOLATILE(hs, ptr) 0
+#endif
+
+/* Return values for http_send_*() */
+#define HTTP_DATA_TO_SEND_BREAK 2
+#define HTTP_DATA_TO_SEND_CONTINUE 1
+#define HTTP_NO_DATA_TO_SEND 0
+
+typedef struct
+{
+ const char *name;
+ u8_t shtml;
+} default_filename;
+
+const default_filename g_psDefaultFilenames[] = {
+ {"/index.shtml", 1 },
+ {"/index.ssi", 1 },
+ {"/index.shtm", 1 },
+ {"/index.html", 0 },
+ {"/index.htm", 0 }
+};
+
+#define NUM_DEFAULT_FILENAMES (sizeof(g_psDefaultFilenames) / \
+ sizeof(default_filename))
+
+#if LWIP_HTTPD_SUPPORT_REQUESTLIST
+/** HTTP request is copied here from pbufs for simple parsing */
+static char httpd_req_buf[LWIP_HTTPD_MAX_REQ_LENGTH+1];
+#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */
+
+#if LWIP_HTTPD_SUPPORT_POST
+#if LWIP_HTTPD_POST_MAX_RESPONSE_URI_LEN > LWIP_HTTPD_MAX_REQUEST_URI_LEN
+#define LWIP_HTTPD_URI_BUF_LEN LWIP_HTTPD_POST_MAX_RESPONSE_URI_LEN
+#endif
+#endif
+#ifndef LWIP_HTTPD_URI_BUF_LEN
+#define LWIP_HTTPD_URI_BUF_LEN LWIP_HTTPD_MAX_REQUEST_URI_LEN
+#endif
+#if LWIP_HTTPD_URI_BUF_LEN
+/* Filename for response file to send when POST is finished or
+ * search for default files when a directory is requested. */
+static char http_uri_buf[LWIP_HTTPD_URI_BUF_LEN+1];
+#endif
+
+#if LWIP_HTTPD_DYNAMIC_HEADERS
+/* The number of individual strings that comprise the headers sent before each
+ * requested file.
+ */
+#define NUM_FILE_HDR_STRINGS 5
+#define HDR_STRINGS_IDX_HTTP_STATUS 0 /* e.g. "HTTP/1.0 200 OK\r\n" */
+#define HDR_STRINGS_IDX_SERVER_NAME 1 /* e.g. "Server: "HTTPD_SERVER_AGENT"\r\n" */
+#define HDR_STRINGS_IDX_CONTENT_LEN_KEPALIVE 2 /* e.g. "Content-Length: xy\r\n" and/or "Connection: keep-alive\r\n" */
+#define HDR_STRINGS_IDX_CONTENT_LEN_NR 3 /* the byte count, when content-length is used */
+#define HDR_STRINGS_IDX_CONTENT_TYPE 4 /* the content type (or default answer content type including default document) */
+
+/* The dynamically generated Content-Length buffer needs space for CRLF + NULL */
+#define LWIP_HTTPD_MAX_CONTENT_LEN_OFFSET 3
+#ifndef LWIP_HTTPD_MAX_CONTENT_LEN_SIZE
+/* The dynamically generated Content-Length buffer shall be able to work with
+ ~953 MB (9 digits) */
+#define LWIP_HTTPD_MAX_CONTENT_LEN_SIZE (9 + LWIP_HTTPD_MAX_CONTENT_LEN_OFFSET)
+#endif
+#endif /* LWIP_HTTPD_DYNAMIC_HEADERS */
+
+#if LWIP_HTTPD_SSI
+
+#define HTTPD_LAST_TAG_PART 0xFFFF
+
+enum tag_check_state {
+ TAG_NONE, /* Not processing an SSI tag */
+ TAG_LEADIN, /* Tag lead in "<!--#" being processed */
+ TAG_FOUND, /* Tag name being read, looking for lead-out start */
+ TAG_LEADOUT, /* Tag lead out "-->" being processed */
+ TAG_SENDING /* Sending tag replacement string */
+};
+
+struct http_ssi_state {
+ const char *parsed; /* Pointer to the first unparsed byte in buf. */
+#if !LWIP_HTTPD_SSI_INCLUDE_TAG
+ const char *tag_started;/* Pointer to the first opening '<' of the tag. */
+#endif /* !LWIP_HTTPD_SSI_INCLUDE_TAG */
+ const char *tag_end; /* Pointer to char after the closing '>' of the tag. */
+ u32_t parse_left; /* Number of unparsed bytes in buf. */
+ u16_t tag_index; /* Counter used by tag parsing state machine */
+ u16_t tag_insert_len; /* Length of insert in string tag_insert */
+#if LWIP_HTTPD_SSI_MULTIPART
+ u16_t tag_part; /* Counter passed to and changed by tag insertion function to insert multiple times */
+#endif /* LWIP_HTTPD_SSI_MULTIPART */
+ u8_t tag_name_len; /* Length of the tag name in string tag_name */
+ char tag_name[LWIP_HTTPD_MAX_TAG_NAME_LEN + 1]; /* Last tag name extracted */
+ char tag_insert[LWIP_HTTPD_MAX_TAG_INSERT_LEN + 1]; /* Insert string for tag_name */
+ enum tag_check_state tag_state; /* State of the tag processor */
+};
+#endif /* LWIP_HTTPD_SSI */
+
+struct http_state {
+#if LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED
+ struct http_state *next;
+#endif /* LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED */
+ struct fs_file file_handle;
+ struct fs_file *handle;
+ const char *file; /* Pointer to first unsent byte in buf. */
+
+ struct tcp_pcb *pcb;
+#if LWIP_HTTPD_SUPPORT_REQUESTLIST
+ struct pbuf *req;
+#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */
+
+#if LWIP_HTTPD_DYNAMIC_FILE_READ
+ char *buf; /* File read buffer. */
+ int buf_len; /* Size of file read buffer, buf. */
+#endif /* LWIP_HTTPD_DYNAMIC_FILE_READ */
+ u32_t left; /* Number of unsent bytes in buf. */
+ u8_t retries;
+#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE
+ u8_t keepalive;
+#endif /* LWIP_HTTPD_SUPPORT_11_KEEPALIVE */
+#if LWIP_HTTPD_SSI
+ struct http_ssi_state *ssi;
+#endif /* LWIP_HTTPD_SSI */
+#if LWIP_HTTPD_CGI
+ char *params[LWIP_HTTPD_MAX_CGI_PARAMETERS]; /* Params extracted from the request URI */
+ char *param_vals[LWIP_HTTPD_MAX_CGI_PARAMETERS]; /* Values for each extracted param */
+#endif /* LWIP_HTTPD_CGI */
+#if LWIP_HTTPD_DYNAMIC_HEADERS
+ const char *hdrs[NUM_FILE_HDR_STRINGS]; /* HTTP headers to be sent. */
+ char hdr_content_len[LWIP_HTTPD_MAX_CONTENT_LEN_SIZE];
+ u16_t hdr_pos; /* The position of the first unsent header byte in the
+ current string */
+ u16_t hdr_index; /* The index of the hdr string currently being sent. */
+#endif /* LWIP_HTTPD_DYNAMIC_HEADERS */
+#if LWIP_HTTPD_TIMING
+ u32_t time_started;
+#endif /* LWIP_HTTPD_TIMING */
+#if LWIP_HTTPD_SUPPORT_POST
+ u32_t post_content_len_left;
+#if LWIP_HTTPD_POST_MANUAL_WND
+ u32_t unrecved_bytes;
+ u8_t no_auto_wnd;
+ u8_t post_finished;
+#endif /* LWIP_HTTPD_POST_MANUAL_WND */
+#endif /* LWIP_HTTPD_SUPPORT_POST*/
+};
+
+#if HTTPD_USE_MEM_POOL
+LWIP_MEMPOOL_DECLARE(HTTPD_STATE, MEMP_NUM_PARALLEL_HTTPD_CONNS, sizeof(struct http_state), "HTTPD_STATE")
+#if LWIP_HTTPD_SSI
+LWIP_MEMPOOL_DECLARE(HTTPD_SSI_STATE, MEMP_NUM_PARALLEL_HTTPD_SSI_CONNS, sizeof(struct http_ssi_state), "HTTPD_SSI_STATE")
+#define HTTP_FREE_SSI_STATE(x) LWIP_MEMPOOL_FREE(HTTPD_SSI_STATE, (x))
+#define HTTP_ALLOC_SSI_STATE() (struct http_ssi_state *)LWIP_MEMPOOL_ALLOC(HTTPD_SSI_STATE)
+#endif /* LWIP_HTTPD_SSI */
+#define HTTP_ALLOC_HTTP_STATE() (struct http_state *)LWIP_MEMPOOL_ALLOC(HTTPD_STATE)
+#define HTTP_FREE_HTTP_STATE(x) LWIP_MEMPOOL_FREE(HTTPD_STATE, (x))
+#else /* HTTPD_USE_MEM_POOL */
+#define HTTP_ALLOC_HTTP_STATE() (struct http_state *)mem_malloc(sizeof(struct http_state))
+#define HTTP_FREE_HTTP_STATE(x) mem_free(x)
+#if LWIP_HTTPD_SSI
+#define HTTP_ALLOC_SSI_STATE() (struct http_ssi_state *)mem_malloc(sizeof(struct http_ssi_state))
+#define HTTP_FREE_SSI_STATE(x) mem_free(x)
+#endif /* LWIP_HTTPD_SSI */
+#endif /* HTTPD_USE_MEM_POOL */
+
+static err_t http_close_conn(struct tcp_pcb *pcb, struct http_state *hs);
+static err_t http_close_or_abort_conn(struct tcp_pcb *pcb, struct http_state *hs, u8_t abort_conn);
+static err_t http_find_file(struct http_state *hs, const char *uri, int is_09);
+static err_t http_init_file(struct http_state *hs, struct fs_file *file, int is_09, const char *uri, u8_t tag_check, char* params);
+static err_t http_poll(void *arg, struct tcp_pcb *pcb);
+static u8_t http_check_eof(struct tcp_pcb *pcb, struct http_state *hs);
+#if LWIP_HTTPD_FS_ASYNC_READ
+static void http_continue(void *connection);
+#endif /* LWIP_HTTPD_FS_ASYNC_READ */
+
+#if LWIP_HTTPD_SSI
+/* SSI insert handler function pointer. */
+tSSIHandler g_pfnSSIHandler;
+#if !LWIP_HTTPD_SSI_RAW
+int g_iNumTags;
+const char **g_ppcTags;
+#endif /* !LWIP_HTTPD_SSI_RAW */
+
+#define LEN_TAG_LEAD_IN 5
+const char * const g_pcTagLeadIn = "<!--#";
+
+#define LEN_TAG_LEAD_OUT 3
+const char * const g_pcTagLeadOut = "-->";
+#endif /* LWIP_HTTPD_SSI */
+
+#if LWIP_HTTPD_CGI
+/* CGI handler information */
+const tCGI *g_pCGIs;
+int g_iNumCGIs;
+int http_cgi_paramcount;
+#define http_cgi_params hs->params
+#define http_cgi_param_vals hs->param_vals
+#elif LWIP_HTTPD_CGI_SSI
+char *http_cgi_params[LWIP_HTTPD_MAX_CGI_PARAMETERS]; /* Params extracted from the request URI */
+char *http_cgi_param_vals[LWIP_HTTPD_MAX_CGI_PARAMETERS]; /* Values for each extracted param */
+#endif /* LWIP_HTTPD_CGI */
+
+#if LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED
+/** global list of active HTTP connections, use to kill the oldest when
+ running out of memory */
+static struct http_state *http_connections;
+
+static void
+http_add_connection(struct http_state *hs)
+{
+ /* add the connection to the list */
+ hs->next = http_connections;
+ http_connections = hs;
+}
+
+static void
+http_remove_connection(struct http_state *hs)
+{
+ /* take the connection off the list */
+ if (http_connections) {
+ if (http_connections == hs) {
+ http_connections = hs->next;
+ } else {
+ struct http_state *last;
+ for(last = http_connections; last->next != NULL; last = last->next) {
+ if (last->next == hs) {
+ last->next = hs->next;
+ break;
+ }
+ }
+ }
+ }
+}
+
+static void
+http_kill_oldest_connection(u8_t ssi_required)
+{
+ struct http_state *hs = http_connections;
+ struct http_state *hs_free_next = NULL;
+ while(hs && hs->next) {
+#if LWIP_HTTPD_SSI
+ if (ssi_required) {
+ if (hs->next->ssi != NULL) {
+ hs_free_next = hs;
+ }
+ } else
+#else /* LWIP_HTTPD_SSI */
+ LWIP_UNUSED_ARG(ssi_required);
+#endif /* LWIP_HTTPD_SSI */
+ {
+ hs_free_next = hs;
+ }
+ LWIP_ASSERT("broken list", hs != hs->next);
+ hs = hs->next;
+ }
+ if (hs_free_next != NULL) {
+ LWIP_ASSERT("hs_free_next->next != NULL", hs_free_next->next != NULL);
+ LWIP_ASSERT("hs_free_next->next->pcb != NULL", hs_free_next->next->pcb != NULL);
+ /* send RST when killing a connection because of memory shortage */
+ http_close_or_abort_conn(hs_free_next->next->pcb, hs_free_next->next, 1); /* this also unlinks the http_state from the list */
+ }
+}
+#else /* LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED */
+
+#define http_add_connection(hs)
+#define http_remove_connection(hs)
+
+#endif /* LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED */
+
+#if LWIP_HTTPD_SSI
+/** Allocate as struct http_ssi_state. */
+static struct http_ssi_state*
+http_ssi_state_alloc(void)
+{
+ struct http_ssi_state *ret = HTTP_ALLOC_SSI_STATE();
+#if LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED
+ if (ret == NULL) {
+ http_kill_oldest_connection(1);
+ ret = HTTP_ALLOC_SSI_STATE();
+ }
+#endif /* LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED */
+ if (ret != NULL) {
+ memset(ret, 0, sizeof(struct http_ssi_state));
+ }
+ return ret;
+}
+
+/** Free a struct http_ssi_state. */
+static void
+http_ssi_state_free(struct http_ssi_state *ssi)
+{
+ if (ssi != NULL) {
+ HTTP_FREE_SSI_STATE(ssi);
+ }
+}
+#endif /* LWIP_HTTPD_SSI */
+
+/** Initialize a struct http_state.
+ */
+static void
+http_state_init(struct http_state* hs)
+{
+ /* Initialize the structure. */
+ memset(hs, 0, sizeof(struct http_state));
+#if LWIP_HTTPD_DYNAMIC_HEADERS
+ /* Indicate that the headers are not yet valid */
+ hs->hdr_index = NUM_FILE_HDR_STRINGS;
+#endif /* LWIP_HTTPD_DYNAMIC_HEADERS */
+}
+
+/** Allocate a struct http_state. */
+static struct http_state*
+http_state_alloc(void)
+{
+ struct http_state *ret = HTTP_ALLOC_HTTP_STATE();
+#if LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED
+ if (ret == NULL) {
+ http_kill_oldest_connection(0);
+ ret = HTTP_ALLOC_HTTP_STATE();
+ }
+#endif /* LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED */
+ if (ret != NULL) {
+ http_state_init(ret);
+ http_add_connection(ret);
+ }
+ return ret;
+}
+
+/** Free a struct http_state.
+ * Also frees the file data if dynamic.
+ */
+static void
+http_state_eof(struct http_state *hs)
+{
+ if(hs->handle) {
+#if LWIP_HTTPD_TIMING
+ u32_t ms_needed = sys_now() - hs->time_started;
+ u32_t needed = LWIP_MAX(1, (ms_needed/100));
+ LWIP_DEBUGF(HTTPD_DEBUG_TIMING, ("httpd: needed %"U32_F" ms to send file of %d bytes -> %"U32_F" bytes/sec\n",
+ ms_needed, hs->handle->len, ((((u32_t)hs->handle->len) * 10) / needed)));
+#endif /* LWIP_HTTPD_TIMING */
+ fs_close(hs->handle);
+ hs->handle = NULL;
+ }
+#if LWIP_HTTPD_DYNAMIC_FILE_READ
+ if (hs->buf != NULL) {
+ mem_free(hs->buf);
+ hs->buf = NULL;
+ }
+#endif /* LWIP_HTTPD_DYNAMIC_FILE_READ */
+#if LWIP_HTTPD_SSI
+ if (hs->ssi) {
+ http_ssi_state_free(hs->ssi);
+ hs->ssi = NULL;
+ }
+#endif /* LWIP_HTTPD_SSI */
+#if LWIP_HTTPD_SUPPORT_REQUESTLIST
+ if (hs->req) {
+ pbuf_free(hs->req);
+ hs->req = NULL;
+ }
+#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */
+}
+
+/** Free a struct http_state.
+ * Also frees the file data if dynamic.
+ */
+static void
+http_state_free(struct http_state *hs)
+{
+ if (hs != NULL) {
+ http_state_eof(hs);
+ http_remove_connection(hs);
+ HTTP_FREE_HTTP_STATE(hs);
+ }
+}
+
+/** Call tcp_write() in a loop trying smaller and smaller length
+ *
+ * @param pcb tcp_pcb to send
+ * @param ptr Data to send
+ * @param length Length of data to send (in/out: on return, contains the
+ * amount of data sent)
+ * @param apiflags directly passed to tcp_write
+ * @return the return value of tcp_write
+ */
+static err_t
+http_write(struct tcp_pcb *pcb, const void* ptr, u16_t *length, u8_t apiflags)
+{
+ u16_t len, max_len;
+ err_t err;
+ LWIP_ASSERT("length != NULL", length != NULL);
+ len = *length;
+ if (len == 0) {
+ return ERR_OK;
+ }
+ /* We cannot send more data than space available in the send buffer. */
+ max_len = tcp_sndbuf(pcb);
+ if (max_len < len) {
+ len = max_len;
+ }
+#ifdef HTTPD_MAX_WRITE_LEN
+ /* Additional limitation: e.g. don't enqueue more than 2*mss at once */
+ max_len = HTTPD_MAX_WRITE_LEN(pcb);
+ if(len > max_len) {
+ len = max_len;
+ }
+#endif /* HTTPD_MAX_WRITE_LEN */
+ do {
+ LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Trying go send %d bytes\n", len));
+ err = tcp_write(pcb, ptr, len, apiflags);
+ if (err == ERR_MEM) {
+ if ((tcp_sndbuf(pcb) == 0) ||
+ (tcp_sndqueuelen(pcb) >= TCP_SND_QUEUELEN)) {
+ /* no need to try smaller sizes */
+ len = 1;
+ } else {
+ len /= 2;
+ }
+ LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE,
+ ("Send failed, trying less (%d bytes)\n", len));
+ }
+ } while ((err == ERR_MEM) && (len > 1));
+
+ if (err == ERR_OK) {
+ LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Sent %d bytes\n", len));
+ *length = len;
+ } else {
+ LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Send failed with err %d (\"%s\")\n", err, lwip_strerr(err)));
+ *length = 0;
+ }
+
+#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE
+ /* ensure nagle is normally enabled (only disabled for persistent connections
+ when all data has been enqueued but the connection stays open for the next
+ request */
+ tcp_nagle_enable(pcb);
+#endif
+
+ return err;
+}
+
+/**
+ * The connection shall be actively closed (using RST to close from fault states).
+ * Reset the sent- and recv-callbacks.
+ *
+ * @param pcb the tcp pcb to reset callbacks
+ * @param hs connection state to free
+ */
+static err_t
+http_close_or_abort_conn(struct tcp_pcb *pcb, struct http_state *hs, u8_t abort_conn)
+{
+ err_t err;
+ LWIP_DEBUGF(HTTPD_DEBUG, ("Closing connection %p\n", (void*)pcb));
+
+#if LWIP_HTTPD_SUPPORT_POST
+ if (hs != NULL) {
+ if ((hs->post_content_len_left != 0)
+#if LWIP_HTTPD_POST_MANUAL_WND
+ || ((hs->no_auto_wnd != 0) && (hs->unrecved_bytes != 0))
+#endif /* LWIP_HTTPD_POST_MANUAL_WND */
+ ) {
+ /* make sure the post code knows that the connection is closed */
+ http_uri_buf[0] = 0;
+ httpd_post_finished(hs, http_uri_buf, LWIP_HTTPD_URI_BUF_LEN);
+ }
+ }
+#endif /* LWIP_HTTPD_SUPPORT_POST*/
+
+
+ tcp_arg(pcb, NULL);
+ tcp_recv(pcb, NULL);
+ tcp_err(pcb, NULL);
+ tcp_poll(pcb, NULL, 0);
+ tcp_sent(pcb, NULL);
+ if (hs != NULL) {
+ http_state_free(hs);
+ }
+
+ if (abort_conn) {
+ tcp_abort(pcb);
+ return ERR_OK;
+ }
+ err = tcp_close(pcb);
+ if (err != ERR_OK) {
+ LWIP_DEBUGF(HTTPD_DEBUG, ("Error %d closing %p\n", err, (void*)pcb));
+ /* error closing, try again later in poll */
+ tcp_poll(pcb, http_poll, HTTPD_POLL_INTERVAL);
+ }
+ return err;
+}
+
+/**
+ * The connection shall be actively closed.
+ * Reset the sent- and recv-callbacks.
+ *
+ * @param pcb the tcp pcb to reset callbacks
+ * @param hs connection state to free
+ */
+static err_t
+http_close_conn(struct tcp_pcb *pcb, struct http_state *hs)
+{
+ return http_close_or_abort_conn(pcb, hs, 0);
+}
+
+/** End of file: either close the connection (Connection: close) or
+ * close the file (Connection: keep-alive)
+ */
+static void
+http_eof(struct tcp_pcb *pcb, struct http_state *hs)
+{
+ /* HTTP/1.1 persistent connection? (Not supported for SSI) */
+#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE
+ if (hs->keepalive) {
+ http_remove_connection(hs);
+
+ http_state_eof(hs);
+ http_state_init(hs);
+ /* restore state: */
+ hs->pcb = pcb;
+ hs->keepalive = 1;
+ http_add_connection(hs);
+ /* ensure nagle doesn't interfere with sending all data as fast as possible: */
+ tcp_nagle_disable(pcb);
+ } else
+#endif /* LWIP_HTTPD_SUPPORT_11_KEEPALIVE */
+ {
+ http_close_conn(pcb, hs);
+ }
+}
+
+#if LWIP_HTTPD_CGI || LWIP_HTTPD_CGI_SSI
+/**
+ * Extract URI parameters from the parameter-part of an URI in the form
+ * "test.cgi?x=y" @todo: better explanation!
+ * Pointers to the parameters are stored in hs->param_vals.
+ *
+ * @param hs http connection state
+ * @param params pointer to the NULL-terminated parameter string from the URI
+ * @return number of parameters extracted
+ */
+static int
+extract_uri_parameters(struct http_state *hs, char *params)
+{
+ char *pair;
+ char *equals;
+ int loop;
+
+ LWIP_UNUSED_ARG(hs);
+
+ /* If we have no parameters at all, return immediately. */
+ if(!params || (params[0] == '\0')) {
+ return(0);
+ }
+
+ /* Get a pointer to our first parameter */
+ pair = params;
+
+ /* Parse up to LWIP_HTTPD_MAX_CGI_PARAMETERS from the passed string and ignore the
+ * remainder (if any) */
+ for(loop = 0; (loop < LWIP_HTTPD_MAX_CGI_PARAMETERS) && pair; loop++) {
+
+ /* Save the name of the parameter */
+ http_cgi_params[loop] = pair;
+
+ /* Remember the start of this name=value pair */
+ equals = pair;
+
+ /* Find the start of the next name=value pair and replace the delimiter
+ * with a 0 to terminate the previous pair string. */
+ pair = strchr(pair, '&');
+ if(pair) {
+ *pair = '\0';
+ pair++;
+ } else {
+ /* We didn't find a new parameter so find the end of the URI and
+ * replace the space with a '\0' */
+ pair = strchr(equals, ' ');
+ if(pair) {
+ *pair = '\0';
+ }
+
+ /* Revert to NULL so that we exit the loop as expected. */
+ pair = NULL;
+ }
+
+ /* Now find the '=' in the previous pair, replace it with '\0' and save
+ * the parameter value string. */
+ equals = strchr(equals, '=');
+ if(equals) {
+ *equals = '\0';
+ http_cgi_param_vals[loop] = equals + 1;
+ } else {
+ http_cgi_param_vals[loop] = NULL;
+ }
+ }
+
+ return loop;
+}
+#endif /* LWIP_HTTPD_CGI || LWIP_HTTPD_CGI_SSI */
+
+#if LWIP_HTTPD_SSI
+/**
+ * Insert a tag (found in an shtml in the form of "<!--#tagname-->" into the file.
+ * The tag's name is stored in ssi->tag_name (NULL-terminated), the replacement
+ * should be written to hs->tag_insert (up to a length of LWIP_HTTPD_MAX_TAG_INSERT_LEN).
+ * The amount of data written is stored to ssi->tag_insert_len.
+ *
+ * @todo: return tag_insert_len - maybe it can be removed from struct http_state?
+ *
+ * @param hs http connection state
+ */
+static void
+get_tag_insert(struct http_state *hs)
+{
+#if LWIP_HTTPD_SSI_RAW
+ const char* tag;
+#else /* LWIP_HTTPD_SSI_RAW */
+ int tag;
+#endif /* LWIP_HTTPD_SSI_RAW */
+ size_t len;
+ struct http_ssi_state *ssi;
+#if LWIP_HTTPD_SSI_MULTIPART
+ u16_t current_tag_part;
+#endif /* LWIP_HTTPD_SSI_MULTIPART */
+
+ LWIP_ASSERT("hs != NULL", hs != NULL);
+ ssi = hs->ssi;
+ LWIP_ASSERT("ssi != NULL", ssi != NULL);
+#if LWIP_HTTPD_SSI_MULTIPART
+ current_tag_part = ssi->tag_part;
+ ssi->tag_part = HTTPD_LAST_TAG_PART;
+#endif /* LWIP_HTTPD_SSI_MULTIPART */
+#if LWIP_HTTPD_SSI_RAW
+ tag = ssi->tag_name;
+#endif
+
+ if(g_pfnSSIHandler
+#if !LWIP_HTTPD_SSI_RAW
+ && g_ppcTags && g_iNumTags
+#endif /* !LWIP_HTTPD_SSI_RAW */
+ ) {
+
+ /* Find this tag in the list we have been provided. */
+#if LWIP_HTTPD_SSI_RAW
+ {
+#else /* LWIP_HTTPD_SSI_RAW */
+ for(tag = 0; tag < g_iNumTags; tag++) {
+ if(strcmp(ssi->tag_name, g_ppcTags[tag]) == 0)
+#endif /* LWIP_HTTPD_SSI_RAW */
+ {
+ ssi->tag_insert_len = g_pfnSSIHandler(tag, ssi->tag_insert,
+ LWIP_HTTPD_MAX_TAG_INSERT_LEN
+#if LWIP_HTTPD_SSI_MULTIPART
+ , current_tag_part, &ssi->tag_part
+#endif /* LWIP_HTTPD_SSI_MULTIPART */
+#if LWIP_HTTPD_FILE_STATE
+ , (hs->handle ? hs->handle->state : NULL)
+#endif /* LWIP_HTTPD_FILE_STATE */
+ );
+#if LWIP_HTTPD_SSI_RAW
+ if (ssi->tag_insert_len != HTTPD_SSI_TAG_UNKNOWN)
+#endif /* LWIP_HTTPD_SSI_RAW */
+ {
+ return;
+ }
+ }
+ }
+ }
+
+ /* If we drop out, we were asked to serve a page which contains tags that
+ * we don't have a handler for. Merely echo back the tags with an error
+ * marker. */
+#define UNKNOWN_TAG1_TEXT "<b>***UNKNOWN TAG "
+#define UNKNOWN_TAG1_LEN 18
+#define UNKNOWN_TAG2_TEXT "***</b>"
+#define UNKNOWN_TAG2_LEN 7
+ len = LWIP_MIN(sizeof(ssi->tag_name), LWIP_MIN(strlen(ssi->tag_name),
+ LWIP_HTTPD_MAX_TAG_INSERT_LEN - (UNKNOWN_TAG1_LEN + UNKNOWN_TAG2_LEN)));
+ MEMCPY(ssi->tag_insert, UNKNOWN_TAG1_TEXT, UNKNOWN_TAG1_LEN);
+ MEMCPY(&ssi->tag_insert[UNKNOWN_TAG1_LEN], ssi->tag_name, len);
+ MEMCPY(&ssi->tag_insert[UNKNOWN_TAG1_LEN + len], UNKNOWN_TAG2_TEXT, UNKNOWN_TAG2_LEN);
+ ssi->tag_insert[UNKNOWN_TAG1_LEN + len + UNKNOWN_TAG2_LEN] = 0;
+
+ len = strlen(ssi->tag_insert);
+ LWIP_ASSERT("len <= 0xffff", len <= 0xffff);
+ ssi->tag_insert_len = (u16_t)len;
+}
+#endif /* LWIP_HTTPD_SSI */
+
+#if LWIP_HTTPD_DYNAMIC_HEADERS
+/**
+ * Generate the relevant HTTP headers for the given filename and write
+ * them into the supplied buffer.
+ */
+static void
+get_http_headers(struct http_state *hs, const char *uri)
+{
+ size_t content_type;
+ char *tmp;
+ char *ext;
+ char *vars;
+ u8_t add_content_len;
+
+ /* In all cases, the second header we send is the server identification
+ so set it here. */
+ hs->hdrs[HDR_STRINGS_IDX_SERVER_NAME] = g_psHTTPHeaderStrings[HTTP_HDR_SERVER];
+ hs->hdrs[HDR_STRINGS_IDX_CONTENT_LEN_KEPALIVE] = NULL;
+ hs->hdrs[HDR_STRINGS_IDX_CONTENT_LEN_NR] = NULL;
+
+ /* Is this a normal file or the special case we use to send back the
+ default "404: Page not found" response? */
+ if (uri == NULL) {
+ hs->hdrs[HDR_STRINGS_IDX_HTTP_STATUS] = g_psHTTPHeaderStrings[HTTP_HDR_NOT_FOUND];
+#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE
+ if (hs->keepalive) {
+ hs->hdrs[HDR_STRINGS_IDX_CONTENT_TYPE] = g_psHTTPHeaderStrings[DEFAULT_404_HTML_PERSISTENT];
+ } else
+#endif
+ {
+ hs->hdrs[HDR_STRINGS_IDX_CONTENT_TYPE] = g_psHTTPHeaderStrings[DEFAULT_404_HTML];
+ }
+
+ /* Set up to send the first header string. */
+ hs->hdr_index = 0;
+ hs->hdr_pos = 0;
+ return;
+ }
+ /* We are dealing with a particular filename. Look for one other
+ special case. We assume that any filename with "404" in it must be
+ indicative of a 404 server error whereas all other files require
+ the 200 OK header. */
+ if (strstr(uri, "404")) {
+ hs->hdrs[HDR_STRINGS_IDX_HTTP_STATUS] = g_psHTTPHeaderStrings[HTTP_HDR_NOT_FOUND];
+ } else if (strstr(uri, "400")) {
+ hs->hdrs[HDR_STRINGS_IDX_HTTP_STATUS] = g_psHTTPHeaderStrings[HTTP_HDR_BAD_REQUEST];
+ } else if (strstr(uri, "501")) {
+ hs->hdrs[HDR_STRINGS_IDX_HTTP_STATUS] = g_psHTTPHeaderStrings[HTTP_HDR_NOT_IMPL];
+ } else {
+ hs->hdrs[HDR_STRINGS_IDX_HTTP_STATUS] = g_psHTTPHeaderStrings[HTTP_HDR_OK];
+ }
+
+ /* Determine if the URI has any variables and, if so, temporarily remove
+ them. */
+ vars = strchr(uri, '?');
+ if(vars) {
+ *vars = '\0';
+ }
+
+ /* Get a pointer to the file extension. We find this by looking for the
+ last occurrence of "." in the filename passed. */
+ ext = NULL;
+ tmp = strchr(uri, '.');
+ while (tmp) {
+ ext = tmp + 1;
+ tmp = strchr(ext, '.');
+ }
+ if (ext != NULL) {
+ /* Now determine the content type and add the relevant header for that. */
+ for (content_type = 0; content_type < NUM_HTTP_HEADERS; content_type++) {
+ /* Have we found a matching extension? */
+ if(!lwip_stricmp(g_psHTTPHeaders[content_type].extension, ext)) {
+ break;
+ }
+ }
+ } else {
+ content_type = NUM_HTTP_HEADERS;
+ }
+
+ /* Reinstate the parameter marker if there was one in the original URI. */
+ if (vars) {
+ *vars = '?';
+ }
+
+#if LWIP_HTTPD_OMIT_HEADER_FOR_EXTENSIONLESS_URI
+ /* Does the URL passed have any file extension? If not, we assume it
+ is a special-case URL used for control state notification and we do
+ not send any HTTP headers with the response. */
+ if (!ext) {
+ /* Force the header index to a value indicating that all headers
+ have already been sent. */
+ hs->hdr_index = NUM_FILE_HDR_STRINGS;
+ return;
+ }
+#endif /* LWIP_HTTPD_OMIT_HEADER_FOR_EXTENSIONLESS_URI */
+ add_content_len = 1;
+ /* Did we find a matching extension? */
+ if(content_type < NUM_HTTP_HEADERS) {
+ /* yes, store it */
+ hs->hdrs[HDR_STRINGS_IDX_CONTENT_TYPE] = g_psHTTPHeaders[content_type].content_type;
+ } else if (!ext) {
+ /* no, no extension found -> use binary transfer to prevent the browser adding '.txt' on save */
+ hs->hdrs[HDR_STRINGS_IDX_CONTENT_TYPE] = HTTP_HDR_APP;
+ } else {
+ /* No - use the default, plain text file type. */
+ hs->hdrs[HDR_STRINGS_IDX_CONTENT_TYPE] = HTTP_HDR_DEFAULT_TYPE;
+ }
+ /* Add content-length header? */
+#if LWIP_HTTPD_SSI
+ if (hs->ssi != NULL) {
+ add_content_len = 0; /* @todo: get maximum file length from SSI */
+ } else
+#endif /* LWIP_HTTPD_SSI */
+ if ((hs->handle == NULL) ||
+ ((hs->handle->flags & (FS_FILE_FLAGS_HEADER_INCLUDED|FS_FILE_FLAGS_HEADER_PERSISTENT)) == FS_FILE_FLAGS_HEADER_INCLUDED)) {
+ add_content_len = 0;
+ }
+ if (add_content_len) {
+ size_t len;
+ lwip_itoa(hs->hdr_content_len, (size_t)LWIP_HTTPD_MAX_CONTENT_LEN_SIZE,
+ hs->handle->len);
+ len = strlen(hs->hdr_content_len);
+ if (len <= LWIP_HTTPD_MAX_CONTENT_LEN_SIZE - LWIP_HTTPD_MAX_CONTENT_LEN_OFFSET) {
+ SMEMCPY(&hs->hdr_content_len[len], CRLF "\0", 3);
+ hs->hdrs[HDR_STRINGS_IDX_CONTENT_LEN_NR] = hs->hdr_content_len;
+ } else {
+ add_content_len = 0;
+ }
+ }
+#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE
+ if (add_content_len) {
+ hs->hdrs[HDR_STRINGS_IDX_CONTENT_LEN_KEPALIVE] = g_psHTTPHeaderStrings[HTTP_HDR_KEEPALIVE_LEN];
+ } else {
+ hs->hdrs[HDR_STRINGS_IDX_CONTENT_LEN_KEPALIVE] = g_psHTTPHeaderStrings[HTTP_HDR_CONN_CLOSE];
+ }
+#else /* LWIP_HTTPD_SUPPORT_11_KEEPALIVE */
+ if (add_content_len) {
+ hs->hdrs[HDR_STRINGS_IDX_CONTENT_LEN_KEPALIVE] = g_psHTTPHeaderStrings[HTTP_HDR_CONTENT_LENGTH];
+ }
+#endif /* LWIP_HTTPD_SUPPORT_11_KEEPALIVE */
+
+ /* Set up to send the first header string. */
+ hs->hdr_index = 0;
+ hs->hdr_pos = 0;
+}
+
+/** Sub-function of http_send(): send dynamic headers
+ *
+ * @returns: - HTTP_NO_DATA_TO_SEND: no new data has been enqueued
+ * - HTTP_DATA_TO_SEND_CONTINUE: continue with sending HTTP body
+ * - HTTP_DATA_TO_SEND_BREAK: data has been enqueued, headers pending,
+ * so don't send HTTP body yet
+ */
+static u8_t
+http_send_headers(struct tcp_pcb *pcb, struct http_state *hs)
+{
+ err_t err;
+ u16_t len;
+ u8_t data_to_send = HTTP_NO_DATA_TO_SEND;
+ u16_t hdrlen, sendlen;
+
+ /* How much data can we send? */
+ len = tcp_sndbuf(pcb);
+ sendlen = len;
+
+ while(len && (hs->hdr_index < NUM_FILE_HDR_STRINGS) && sendlen) {
+ const void *ptr;
+ u16_t old_sendlen;
+ u8_t apiflags;
+ /* How much do we have to send from the current header? */
+ hdrlen = (u16_t)strlen(hs->hdrs[hs->hdr_index]);
+
+ /* How much of this can we send? */
+ sendlen = (len < (hdrlen - hs->hdr_pos)) ? len : (hdrlen - hs->hdr_pos);
+
+ /* Send this amount of data or as much as we can given memory
+ * constraints. */
+ ptr = (const void *)(hs->hdrs[hs->hdr_index] + hs->hdr_pos);
+ old_sendlen = sendlen;
+ apiflags = HTTP_IS_HDR_VOLATILE(hs, ptr);
+ if (hs->hdr_index == HDR_STRINGS_IDX_CONTENT_LEN_NR) {
+ /* content-length is always volatile */
+ apiflags |= TCP_WRITE_FLAG_COPY;
+ }
+ if (hs->hdr_index < NUM_FILE_HDR_STRINGS - 1) {
+ apiflags |= TCP_WRITE_FLAG_MORE;
+ }
+ err = http_write(pcb, ptr, &sendlen, apiflags);
+ if ((err == ERR_OK) && (old_sendlen != sendlen)) {
+ /* Remember that we added some more data to be transmitted. */
+ data_to_send = HTTP_DATA_TO_SEND_CONTINUE;
+ } else if (err != ERR_OK) {
+ /* special case: http_write does not try to send 1 byte */
+ sendlen = 0;
+ }
+
+ /* Fix up the header position for the next time round. */
+ hs->hdr_pos += sendlen;
+ len -= sendlen;
+
+ /* Have we finished sending this string? */
+ if(hs->hdr_pos == hdrlen) {
+ /* Yes - move on to the next one */
+ hs->hdr_index++;
+ /* skip headers that are NULL (not all headers are required) */
+ while ((hs->hdr_index < NUM_FILE_HDR_STRINGS) &&
+ (hs->hdrs[hs->hdr_index] == NULL)) {
+ hs->hdr_index++;
+ }
+ hs->hdr_pos = 0;
+ }
+ }
+
+ if ((hs->hdr_index >= NUM_FILE_HDR_STRINGS) && (hs->file == NULL)) {
+ /* When we are at the end of the headers, check for data to send
+ * instead of waiting for ACK from remote side to continue
+ * (which would happen when sending files from async read). */
+ if(http_check_eof(pcb, hs)) {
+ data_to_send = HTTP_DATA_TO_SEND_CONTINUE;
+ }
+ }
+ /* If we get here and there are still header bytes to send, we send
+ * the header information we just wrote immediately. If there are no
+ * more headers to send, but we do have file data to send, drop through
+ * to try to send some file data too. */
+ if((hs->hdr_index < NUM_FILE_HDR_STRINGS) || !hs->file) {
+ LWIP_DEBUGF(HTTPD_DEBUG, ("tcp_output\n"));
+ return HTTP_DATA_TO_SEND_BREAK;
+ }
+ return data_to_send;
+}
+#endif /* LWIP_HTTPD_DYNAMIC_HEADERS */
+
+/** Sub-function of http_send(): end-of-file (or block) is reached,
+ * either close the file or read the next block (if supported).
+ *
+ * @returns: 0 if the file is finished or no data has been read
+ * 1 if the file is not finished and data has been read
+ */
+static u8_t
+http_check_eof(struct tcp_pcb *pcb, struct http_state *hs)
+{
+ int bytes_left;
+#if LWIP_HTTPD_DYNAMIC_FILE_READ
+ int count;
+#ifdef HTTPD_MAX_WRITE_LEN
+ int max_write_len;
+#endif /* HTTPD_MAX_WRITE_LEN */
+#endif /* LWIP_HTTPD_DYNAMIC_FILE_READ */
+
+ /* Do we have a valid file handle? */
+ if (hs->handle == NULL) {
+ /* No - close the connection. */
+ http_eof(pcb, hs);
+ return 0;
+ }
+ bytes_left = fs_bytes_left(hs->handle);
+ if (bytes_left <= 0) {
+ /* We reached the end of the file so this request is done. */
+ LWIP_DEBUGF(HTTPD_DEBUG, ("End of file.\n"));
+ http_eof(pcb, hs);
+ return 0;
+ }
+#if LWIP_HTTPD_DYNAMIC_FILE_READ
+ /* Do we already have a send buffer allocated? */
+ if(hs->buf) {
+ /* Yes - get the length of the buffer */
+ count = LWIP_MIN(hs->buf_len, bytes_left);
+ } else {
+ /* We don't have a send buffer so allocate one now */
+ count = tcp_sndbuf(pcb);
+ if(bytes_left < count) {
+ count = bytes_left;
+ }
+#ifdef HTTPD_MAX_WRITE_LEN
+ /* Additional limitation: e.g. don't enqueue more than 2*mss at once */
+ max_write_len = HTTPD_MAX_WRITE_LEN(pcb);
+ if (count > max_write_len) {
+ count = max_write_len;
+ }
+#endif /* HTTPD_MAX_WRITE_LEN */
+ do {
+ hs->buf = (char*)mem_malloc((mem_size_t)count);
+ if (hs->buf != NULL) {
+ hs->buf_len = count;
+ break;
+ }
+ count = count / 2;
+ } while (count > 100);
+
+ /* Did we get a send buffer? If not, return immediately. */
+ if (hs->buf == NULL) {
+ LWIP_DEBUGF(HTTPD_DEBUG, ("No buff\n"));
+ return 0;
+ }
+ }
+
+ /* Read a block of data from the file. */
+ LWIP_DEBUGF(HTTPD_DEBUG, ("Trying to read %d bytes.\n", count));
+
+#if LWIP_HTTPD_FS_ASYNC_READ
+ count = fs_read_async(hs->handle, hs->buf, count, http_continue, hs);
+#else /* LWIP_HTTPD_FS_ASYNC_READ */
+ count = fs_read(hs->handle, hs->buf, count);
+#endif /* LWIP_HTTPD_FS_ASYNC_READ */
+ if (count < 0) {
+ if (count == FS_READ_DELAYED) {
+ /* Delayed read, wait for FS to unblock us */
+ return 0;
+ }
+ /* We reached the end of the file so this request is done.
+ * @todo: close here for HTTP/1.1 when reading file fails */
+ LWIP_DEBUGF(HTTPD_DEBUG, ("End of file.\n"));
+ http_eof(pcb, hs);
+ return 0;
+ }
+
+ /* Set up to send the block of data we just read */
+ LWIP_DEBUGF(HTTPD_DEBUG, ("Read %d bytes.\n", count));
+ hs->left = count;
+ hs->file = hs->buf;
+#if LWIP_HTTPD_SSI
+ if (hs->ssi) {
+ hs->ssi->parse_left = count;
+ hs->ssi->parsed = hs->buf;
+ }
+#endif /* LWIP_HTTPD_SSI */
+#else /* LWIP_HTTPD_DYNAMIC_FILE_READ */
+ LWIP_ASSERT("SSI and DYNAMIC_HEADERS turned off but eof not reached", 0);
+#endif /* LWIP_HTTPD_SSI || LWIP_HTTPD_DYNAMIC_HEADERS */
+ return 1;
+}
+
+/** Sub-function of http_send(): This is the normal send-routine for non-ssi files
+ *
+ * @returns: - 1: data has been written (so call tcp_ouput)
+ * - 0: no data has been written (no need to call tcp_output)
+ */
+static u8_t
+http_send_data_nonssi(struct tcp_pcb *pcb, struct http_state *hs)
+{
+ err_t err;
+ u16_t len;
+ u8_t data_to_send = 0;
+
+ /* We are not processing an SHTML file so no tag checking is necessary.
+ * Just send the data as we received it from the file. */
+ len = (u16_t)LWIP_MIN(hs->left, 0xffff);
+
+ err = http_write(pcb, hs->file, &len, HTTP_IS_DATA_VOLATILE(hs));
+ if (err == ERR_OK) {
+ data_to_send = 1;
+ hs->file += len;
+ hs->left -= len;
+ }
+
+ return data_to_send;
+}
+
+#if LWIP_HTTPD_SSI
+/** Sub-function of http_send(): This is the send-routine for ssi files
+ *
+ * @returns: - 1: data has been written (so call tcp_ouput)
+ * - 0: no data has been written (no need to call tcp_output)
+ */
+static u8_t
+http_send_data_ssi(struct tcp_pcb *pcb, struct http_state *hs)
+{
+ err_t err = ERR_OK;
+ u16_t len;
+ u8_t data_to_send = 0;
+
+ struct http_ssi_state *ssi = hs->ssi;
+ LWIP_ASSERT("ssi != NULL", ssi != NULL);
+ /* We are processing an SHTML file so need to scan for tags and replace
+ * them with insert strings. We need to be careful here since a tag may
+ * straddle the boundary of two blocks read from the file and we may also
+ * have to split the insert string between two tcp_write operations. */
+
+ /* How much data could we send? */
+ len = tcp_sndbuf(pcb);
+
+ /* Do we have remaining data to send before parsing more? */
+ if(ssi->parsed > hs->file) {
+ len = (u16_t)LWIP_MIN(ssi->parsed - hs->file, 0xffff);
+
+ err = http_write(pcb, hs->file, &len, HTTP_IS_DATA_VOLATILE(hs));
+ if (err == ERR_OK) {
+ data_to_send = 1;
+ hs->file += len;
+ hs->left -= len;
+ }
+
+ /* If the send buffer is full, return now. */
+ if(tcp_sndbuf(pcb) == 0) {
+ return data_to_send;
+ }
+ }
+
+ LWIP_DEBUGF(HTTPD_DEBUG, ("State %d, %d left\n", ssi->tag_state, (int)ssi->parse_left));
+
+ /* We have sent all the data that was already parsed so continue parsing
+ * the buffer contents looking for SSI tags. */
+ while((ssi->parse_left) && (err == ERR_OK)) {
+ if (len == 0) {
+ return data_to_send;
+ }
+ switch(ssi->tag_state) {
+ case TAG_NONE:
+ /* We are not currently processing an SSI tag so scan for the
+ * start of the lead-in marker. */
+ if(*ssi->parsed == g_pcTagLeadIn[0]) {
+ /* We found what could be the lead-in for a new tag so change
+ * state appropriately. */
+ ssi->tag_state = TAG_LEADIN;
+ ssi->tag_index = 1;
+#if !LWIP_HTTPD_SSI_INCLUDE_TAG
+ ssi->tag_started = ssi->parsed;
+#endif /* !LWIP_HTTPD_SSI_INCLUDE_TAG */
+ }
+
+ /* Move on to the next character in the buffer */
+ ssi->parse_left--;
+ ssi->parsed++;
+ break;
+
+ case TAG_LEADIN:
+ /* We are processing the lead-in marker, looking for the start of
+ * the tag name. */
+
+ /* Have we reached the end of the leadin? */
+ if(ssi->tag_index == LEN_TAG_LEAD_IN) {
+ ssi->tag_index = 0;
+ ssi->tag_state = TAG_FOUND;
+ } else {
+ /* Have we found the next character we expect for the tag leadin? */
+ if(*ssi->parsed == g_pcTagLeadIn[ssi->tag_index]) {
+ /* Yes - move to the next one unless we have found the complete
+ * leadin, in which case we start looking for the tag itself */
+ ssi->tag_index++;
+ } else {
+ /* We found an unexpected character so this is not a tag. Move
+ * back to idle state. */
+ ssi->tag_state = TAG_NONE;
+ }
+
+ /* Move on to the next character in the buffer */
+ ssi->parse_left--;
+ ssi->parsed++;
+ }
+ break;
+
+ case TAG_FOUND:
+ /* We are reading the tag name, looking for the start of the
+ * lead-out marker and removing any whitespace found. */
+
+ /* Remove leading whitespace between the tag leading and the first
+ * tag name character. */
+ if((ssi->tag_index == 0) && ((*ssi->parsed == ' ') ||
+ (*ssi->parsed == '\t') || (*ssi->parsed == '\n') ||
+ (*ssi->parsed == '\r'))) {
+ /* Move on to the next character in the buffer */
+ ssi->parse_left--;
+ ssi->parsed++;
+ break;
+ }
+
+ /* Have we found the end of the tag name? This is signalled by
+ * us finding the first leadout character or whitespace */
+ if((*ssi->parsed == g_pcTagLeadOut[0]) ||
+ (*ssi->parsed == ' ') || (*ssi->parsed == '\t') ||
+ (*ssi->parsed == '\n') || (*ssi->parsed == '\r')) {
+
+ if(ssi->tag_index == 0) {
+ /* We read a zero length tag so ignore it. */
+ ssi->tag_state = TAG_NONE;
+ } else {
+ /* We read a non-empty tag so go ahead and look for the
+ * leadout string. */
+ ssi->tag_state = TAG_LEADOUT;
+ LWIP_ASSERT("ssi->tag_index <= 0xff", ssi->tag_index <= 0xff);
+ ssi->tag_name_len = (u8_t)ssi->tag_index;
+ ssi->tag_name[ssi->tag_index] = '\0';
+ if(*ssi->parsed == g_pcTagLeadOut[0]) {
+ ssi->tag_index = 1;
+ } else {
+ ssi->tag_index = 0;
+ }
+ }
+ } else {
+ /* This character is part of the tag name so save it */
+ if(ssi->tag_index < LWIP_HTTPD_MAX_TAG_NAME_LEN) {
+ ssi->tag_name[ssi->tag_index++] = *ssi->parsed;
+ } else {
+ /* The tag was too long so ignore it. */
+ ssi->tag_state = TAG_NONE;
+ }
+ }
+
+ /* Move on to the next character in the buffer */
+ ssi->parse_left--;
+ ssi->parsed++;
+
+ break;
+
+ /* We are looking for the end of the lead-out marker. */
+ case TAG_LEADOUT:
+ /* Remove leading whitespace between the tag leading and the first
+ * tag leadout character. */
+ if((ssi->tag_index == 0) && ((*ssi->parsed == ' ') ||
+ (*ssi->parsed == '\t') || (*ssi->parsed == '\n') ||
+ (*ssi->parsed == '\r'))) {
+ /* Move on to the next character in the buffer */
+ ssi->parse_left--;
+ ssi->parsed++;
+ break;
+ }
+
+ /* Have we found the next character we expect for the tag leadout? */
+ if(*ssi->parsed == g_pcTagLeadOut[ssi->tag_index]) {
+ /* Yes - move to the next one unless we have found the complete
+ * leadout, in which case we need to call the client to process
+ * the tag. */
+
+ /* Move on to the next character in the buffer */
+ ssi->parse_left--;
+ ssi->parsed++;
+
+ if(ssi->tag_index == (LEN_TAG_LEAD_OUT - 1)) {
+ /* Call the client to ask for the insert string for the
+ * tag we just found. */
+#if LWIP_HTTPD_SSI_MULTIPART
+ ssi->tag_part = 0; /* start with tag part 0 */
+#endif /* LWIP_HTTPD_SSI_MULTIPART */
+ get_tag_insert(hs);
+
+ /* Next time through, we are going to be sending data
+ * immediately, either the end of the block we start
+ * sending here or the insert string. */
+ ssi->tag_index = 0;
+ ssi->tag_state = TAG_SENDING;
+ ssi->tag_end = ssi->parsed;
+#if !LWIP_HTTPD_SSI_INCLUDE_TAG
+ ssi->parsed = ssi->tag_started;
+#endif /* !LWIP_HTTPD_SSI_INCLUDE_TAG*/
+
+ /* If there is any unsent data in the buffer prior to the
+ * tag, we need to send it now. */
+ if (ssi->tag_end > hs->file) {
+ /* How much of the data can we send? */
+#if LWIP_HTTPD_SSI_INCLUDE_TAG
+ len = (u16_t)LWIP_MIN(ssi->tag_end - hs->file, 0xffff);
+#else /* LWIP_HTTPD_SSI_INCLUDE_TAG*/
+ /* we would include the tag in sending */
+ len = (u16_t)LWIP_MIN(ssi->tag_started - hs->file, 0xffff);
+#endif /* LWIP_HTTPD_SSI_INCLUDE_TAG*/
+
+ err = http_write(pcb, hs->file, &len, HTTP_IS_DATA_VOLATILE(hs));
+ if (err == ERR_OK) {
+ data_to_send = 1;
+#if !LWIP_HTTPD_SSI_INCLUDE_TAG
+ if(ssi->tag_started <= hs->file) {
+ /* pretend to have sent the tag, too */
+ len += ssi->tag_end - ssi->tag_started;
+ }
+#endif /* !LWIP_HTTPD_SSI_INCLUDE_TAG*/
+ hs->file += len;
+ hs->left -= len;
+ }
+ }
+ } else {
+ ssi->tag_index++;
+ }
+ } else {
+ /* We found an unexpected character so this is not a tag. Move
+ * back to idle state. */
+ ssi->parse_left--;
+ ssi->parsed++;
+ ssi->tag_state = TAG_NONE;
+ }
+ break;
+
+ /*
+ * We have found a valid tag and are in the process of sending
+ * data as a result of that discovery. We send either remaining data
+ * from the file prior to the insert point or the insert string itself.
+ */
+ case TAG_SENDING:
+ /* Do we have any remaining file data to send from the buffer prior
+ * to the tag? */
+ if(ssi->tag_end > hs->file) {
+ /* How much of the data can we send? */
+#if LWIP_HTTPD_SSI_INCLUDE_TAG
+ len = (u16_t)LWIP_MIN(ssi->tag_end - hs->file, 0xffff);
+#else /* LWIP_HTTPD_SSI_INCLUDE_TAG*/
+ LWIP_ASSERT("hs->started >= hs->file", ssi->tag_started >= hs->file);
+ /* we would include the tag in sending */
+ len = (u16_t)LWIP_MIN(ssi->tag_started - hs->file, 0xffff);
+#endif /* LWIP_HTTPD_SSI_INCLUDE_TAG*/
+ if (len != 0) {
+ err = http_write(pcb, hs->file, &len, HTTP_IS_DATA_VOLATILE(hs));
+ } else {
+ err = ERR_OK;
+ }
+ if (err == ERR_OK) {
+ data_to_send = 1;
+#if !LWIP_HTTPD_SSI_INCLUDE_TAG
+ if(ssi->tag_started <= hs->file) {
+ /* pretend to have sent the tag, too */
+ len += ssi->tag_end - ssi->tag_started;
+ }
+#endif /* !LWIP_HTTPD_SSI_INCLUDE_TAG*/
+ hs->file += len;
+ hs->left -= len;
+ }
+ } else {
+#if LWIP_HTTPD_SSI_MULTIPART
+ if(ssi->tag_index >= ssi->tag_insert_len) {
+ /* Did the last SSIHandler have more to send? */
+ if (ssi->tag_part != HTTPD_LAST_TAG_PART) {
+ /* If so, call it again */
+ ssi->tag_index = 0;
+ get_tag_insert(hs);
+ }
+ }
+#endif /* LWIP_HTTPD_SSI_MULTIPART */
+
+ /* Do we still have insert data left to send? */
+ if(ssi->tag_index < ssi->tag_insert_len) {
+ /* We are sending the insert string itself. How much of the
+ * insert can we send? */
+ len = (ssi->tag_insert_len - ssi->tag_index);
+
+ /* Note that we set the copy flag here since we only have a
+ * single tag insert buffer per connection. If we don't do
+ * this, insert corruption can occur if more than one insert
+ * is processed before we call tcp_output. */
+ err = http_write(pcb, &(ssi->tag_insert[ssi->tag_index]), &len,
+ HTTP_IS_TAG_VOLATILE(hs));
+ if (err == ERR_OK) {
+ data_to_send = 1;
+ ssi->tag_index += len;
+ /* Don't return here: keep on sending data */
+ }
+ } else {
+#if LWIP_HTTPD_SSI_MULTIPART
+ if (ssi->tag_part == HTTPD_LAST_TAG_PART)
+#endif /* LWIP_HTTPD_SSI_MULTIPART */
+ {
+ /* We have sent all the insert data so go back to looking for
+ * a new tag. */
+ LWIP_DEBUGF(HTTPD_DEBUG, ("Everything sent.\n"));
+ ssi->tag_index = 0;
+ ssi->tag_state = TAG_NONE;
+#if !LWIP_HTTPD_SSI_INCLUDE_TAG
+ ssi->parsed = ssi->tag_end;
+#endif /* !LWIP_HTTPD_SSI_INCLUDE_TAG*/
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ /* If we drop out of the end of the for loop, this implies we must have
+ * file data to send so send it now. In TAG_SENDING state, we've already
+ * handled this so skip the send if that's the case. */
+ if((ssi->tag_state != TAG_SENDING) && (ssi->parsed > hs->file)) {
+ len = (u16_t)LWIP_MIN(ssi->parsed - hs->file, 0xffff);
+
+ err = http_write(pcb, hs->file, &len, HTTP_IS_DATA_VOLATILE(hs));
+ if (err == ERR_OK) {
+ data_to_send = 1;
+ hs->file += len;
+ hs->left -= len;
+ }
+ }
+ return data_to_send;
+}
+#endif /* LWIP_HTTPD_SSI */
+
+/**
+ * Try to send more data on this pcb.
+ *
+ * @param pcb the pcb to send data
+ * @param hs connection state
+ */
+static u8_t
+http_send(struct tcp_pcb *pcb, struct http_state *hs)
+{
+ u8_t data_to_send = HTTP_NO_DATA_TO_SEND;
+
+ LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("http_send: pcb=%p hs=%p left=%d\n", (void*)pcb,
+ (void*)hs, hs != NULL ? (int)hs->left : 0));
+
+#if LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND
+ if (hs->unrecved_bytes != 0) {
+ return 0;
+ }
+#endif /* LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND */
+
+ /* If we were passed a NULL state structure pointer, ignore the call. */
+ if (hs == NULL) {
+ return 0;
+ }
+
+#if LWIP_HTTPD_FS_ASYNC_READ
+ /* Check if we are allowed to read from this file.
+ (e.g. SSI might want to delay sending until data is available) */
+ if (!fs_is_file_ready(hs->handle, http_continue, hs)) {
+ return 0;
+ }
+#endif /* LWIP_HTTPD_FS_ASYNC_READ */
+
+#if LWIP_HTTPD_DYNAMIC_HEADERS
+ /* Do we have any more header data to send for this file? */
+ if (hs->hdr_index < NUM_FILE_HDR_STRINGS) {
+ data_to_send = http_send_headers(pcb, hs);
+ if ((data_to_send != HTTP_DATA_TO_SEND_CONTINUE) &&
+ (hs->hdr_index < NUM_FILE_HDR_STRINGS)) {
+ return data_to_send;
+ }
+ }
+#endif /* LWIP_HTTPD_DYNAMIC_HEADERS */
+
+ /* Have we run out of file data to send? If so, we need to read the next
+ * block from the file. */
+ if (hs->left == 0) {
+ if (!http_check_eof(pcb, hs)) {
+ return 0;
+ }
+ }
+
+#if LWIP_HTTPD_SSI
+ if(hs->ssi) {
+ data_to_send = http_send_data_ssi(pcb, hs);
+ } else
+#endif /* LWIP_HTTPD_SSI */
+ {
+ data_to_send = http_send_data_nonssi(pcb, hs);
+ }
+
+ if((hs->left == 0) && (fs_bytes_left(hs->handle) <= 0)) {
+ /* We reached the end of the file so this request is done.
+ * This adds the FIN flag right into the last data segment. */
+ LWIP_DEBUGF(HTTPD_DEBUG, ("End of file.\n"));
+ http_eof(pcb, hs);
+ return 0;
+ }
+ LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("send_data end.\n"));
+ return data_to_send;
+}
+
+#if LWIP_HTTPD_SUPPORT_EXTSTATUS
+/** Initialize a http connection with a file to send for an error message
+ *
+ * @param hs http connection state
+ * @param error_nr HTTP error number
+ * @return ERR_OK if file was found and hs has been initialized correctly
+ * another err_t otherwise
+ */
+static err_t
+http_find_error_file(struct http_state *hs, u16_t error_nr)
+{
+ const char *uri1, *uri2, *uri3;
+ err_t err;
+
+ if (error_nr == 501) {
+ uri1 = "/501.html";
+ uri2 = "/501.htm";
+ uri3 = "/501.shtml";
+ } else {
+ /* 400 (bad request is the default) */
+ uri1 = "/400.html";
+ uri2 = "/400.htm";
+ uri3 = "/400.shtml";
+ }
+ err = fs_open(&hs->file_handle, uri1);
+ if (err != ERR_OK) {
+ err = fs_open(&hs->file_handle, uri2);
+ if (err != ERR_OK) {
+ err = fs_open(&hs->file_handle, uri3);
+ if (err != ERR_OK) {
+ LWIP_DEBUGF(HTTPD_DEBUG, ("Error page for error %"U16_F" not found\n",
+ error_nr));
+ return ERR_ARG;
+ }
+ }
+ }
+ return http_init_file(hs, &hs->file_handle, 0, NULL, 0, NULL);
+}
+#else /* LWIP_HTTPD_SUPPORT_EXTSTATUS */
+#define http_find_error_file(hs, error_nr) ERR_ARG
+#endif /* LWIP_HTTPD_SUPPORT_EXTSTATUS */
+
+/**
+ * Get the file struct for a 404 error page.
+ * Tries some file names and returns NULL if none found.
+ *
+ * @param uri pointer that receives the actual file name URI
+ * @return file struct for the error page or NULL no matching file was found
+ */
+static struct fs_file *
+http_get_404_file(struct http_state *hs, const char **uri)
+{
+ err_t err;
+
+ *uri = "/404.html";
+ err = fs_open(&hs->file_handle, *uri);
+ if (err != ERR_OK) {
+ /* 404.html doesn't exist. Try 404.htm instead. */
+ *uri = "/404.htm";
+ err = fs_open(&hs->file_handle, *uri);
+ if (err != ERR_OK) {
+ /* 404.htm doesn't exist either. Try 404.shtml instead. */
+ *uri = "/404.shtml";
+ err = fs_open(&hs->file_handle, *uri);
+ if (err != ERR_OK) {
+ /* 404.htm doesn't exist either. Indicate to the caller that it should
+ * send back a default 404 page.
+ */
+ *uri = NULL;
+ return NULL;
+ }
+ }
+ }
+
+ return &hs->file_handle;
+}
+
+#if LWIP_HTTPD_SUPPORT_POST
+static err_t
+http_handle_post_finished(struct http_state *hs)
+{
+#if LWIP_HTTPD_POST_MANUAL_WND
+ /* Prevent multiple calls to httpd_post_finished, since it might have already
+ been called before from httpd_post_data_recved(). */
+ if (hs->post_finished) {
+ return ERR_OK;
+ }
+ hs->post_finished = 1;
+#endif /* LWIP_HTTPD_POST_MANUAL_WND */
+ /* application error or POST finished */
+ /* NULL-terminate the buffer */
+ http_uri_buf[0] = 0;
+ httpd_post_finished(hs, http_uri_buf, LWIP_HTTPD_URI_BUF_LEN);
+ return http_find_file(hs, http_uri_buf, 0);
+}
+
+/** Pass received POST body data to the application and correctly handle
+ * returning a response document or closing the connection.
+ * ATTENTION: The application is responsible for the pbuf now, so don't free it!
+ *
+ * @param hs http connection state
+ * @param p pbuf to pass to the application
+ * @return ERR_OK if passed successfully, another err_t if the response file
+ * hasn't been found (after POST finished)
+ */
+static err_t
+http_post_rxpbuf(struct http_state *hs, struct pbuf *p)
+{
+ err_t err;
+
+ if (p != NULL) {
+ /* adjust remaining Content-Length */
+ if (hs->post_content_len_left < p->tot_len) {
+ hs->post_content_len_left = 0;
+ } else {
+ hs->post_content_len_left -= p->tot_len;
+ }
+ }
+#if LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND
+ /* prevent connection being closed if httpd_post_data_recved() is called nested */
+ hs->unrecved_bytes++;
+#endif
+ err = httpd_post_receive_data(hs, p);
+#if LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND
+ hs->unrecved_bytes--;
+#endif
+ if (err != ERR_OK) {
+ /* Ignore remaining content in case of application error */
+ hs->post_content_len_left = 0;
+ }
+ if (hs->post_content_len_left == 0) {
+#if LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND
+ if (hs->unrecved_bytes != 0) {
+ return ERR_OK;
+ }
+#endif /* LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND */
+ /* application error or POST finished */
+ return http_handle_post_finished(hs);
+ }
+
+ return ERR_OK;
+}
+
+/** Handle a post request. Called from http_parse_request when method 'POST'
+ * is found.
+ *
+ * @param p The input pbuf (containing the POST header and body).
+ * @param hs The http connection state.
+ * @param data HTTP request (header and part of body) from input pbuf(s).
+ * @param data_len Size of 'data'.
+ * @param uri The HTTP URI parsed from input pbuf(s).
+ * @param uri_end Pointer to the end of 'uri' (here, the rest of the HTTP
+ * header starts).
+ * @return ERR_OK: POST correctly parsed and accepted by the application.
+ * ERR_INPROGRESS: POST not completely parsed (no error yet)
+ * another err_t: Error parsing POST or denied by the application
+ */
+static err_t
+http_post_request(struct pbuf *inp, struct http_state *hs,
+ char *data, u16_t data_len, char *uri, char *uri_end)
+{
+ err_t err;
+ /* search for end-of-header (first double-CRLF) */
+ char* crlfcrlf = lwip_strnstr(uri_end + 1, CRLF CRLF, data_len - (uri_end + 1 - data));
+
+ if (crlfcrlf != NULL) {
+ /* search for "Content-Length: " */
+#define HTTP_HDR_CONTENT_LEN "Content-Length: "
+#define HTTP_HDR_CONTENT_LEN_LEN 16
+#define HTTP_HDR_CONTENT_LEN_DIGIT_MAX_LEN 10
+ char *scontent_len = lwip_strnstr(uri_end + 1, HTTP_HDR_CONTENT_LEN, crlfcrlf - (uri_end + 1));
+ if (scontent_len != NULL) {
+ char *scontent_len_end = lwip_strnstr(scontent_len + HTTP_HDR_CONTENT_LEN_LEN, CRLF, HTTP_HDR_CONTENT_LEN_DIGIT_MAX_LEN);
+ if (scontent_len_end != NULL) {
+ int content_len;
+ char *content_len_num = scontent_len + HTTP_HDR_CONTENT_LEN_LEN;
+ content_len = atoi(content_len_num);
+ if (content_len == 0) {
+ /* if atoi returns 0 on error, fix this */
+ if ((content_len_num[0] != '0') || (content_len_num[1] != '\r')) {
+ content_len = -1;
+ }
+ }
+ if (content_len >= 0) {
+ /* adjust length of HTTP header passed to application */
+ const char *hdr_start_after_uri = uri_end + 1;
+ u16_t hdr_len = (u16_t)LWIP_MIN(data_len, crlfcrlf + 4 - data);
+ u16_t hdr_data_len = (u16_t)LWIP_MIN(data_len, crlfcrlf + 4 - hdr_start_after_uri);
+ u8_t post_auto_wnd = 1;
+ http_uri_buf[0] = 0;
+ /* trim http header */
+ *crlfcrlf = 0;
+ err = httpd_post_begin(hs, uri, hdr_start_after_uri, hdr_data_len, content_len,
+ http_uri_buf, LWIP_HTTPD_URI_BUF_LEN, &post_auto_wnd);
+ if (err == ERR_OK) {
+ /* try to pass in data of the first pbuf(s) */
+ struct pbuf *q = inp;
+ u16_t start_offset = hdr_len;
+#if LWIP_HTTPD_POST_MANUAL_WND
+ hs->no_auto_wnd = !post_auto_wnd;
+#endif /* LWIP_HTTPD_POST_MANUAL_WND */
+ /* set the Content-Length to be received for this POST */
+ hs->post_content_len_left = (u32_t)content_len;
+
+ /* get to the pbuf where the body starts */
+ while((q != NULL) && (q->len <= start_offset)) {
+ start_offset -= q->len;
+ q = q->next;
+ }
+ if (q != NULL) {
+ /* hide the remaining HTTP header */
+ pbuf_header(q, -(s16_t)start_offset);
+#if LWIP_HTTPD_POST_MANUAL_WND
+ if (!post_auto_wnd) {
+ /* already tcp_recved() this data... */
+ hs->unrecved_bytes = q->tot_len;
+ }
+#endif /* LWIP_HTTPD_POST_MANUAL_WND */
+ pbuf_ref(q);
+ return http_post_rxpbuf(hs, q);
+ } else if (hs->post_content_len_left == 0) {
+ q = pbuf_alloc(PBUF_RAW, 0, PBUF_REF);
+ return http_post_rxpbuf(hs, q);
+ } else {
+ return ERR_OK;
+ }
+ } else {
+ /* return file passed from application */
+ return http_find_file(hs, http_uri_buf, 0);
+ }
+ } else {
+ LWIP_DEBUGF(HTTPD_DEBUG, ("POST received invalid Content-Length: %s\n",
+ content_len_num));
+ return ERR_ARG;
+ }
+ }
+ }
+ /* If we come here, headers are fully received (double-crlf), but Content-Length
+ was not included. Since this is currently the only supported method, we have
+ to fail in this case! */
+ LWIP_DEBUGF(HTTPD_DEBUG, ("Error when parsing Content-Length\n"));
+ return ERR_ARG;
+ }
+ /* if we come here, the POST is incomplete */
+#if LWIP_HTTPD_SUPPORT_REQUESTLIST
+ return ERR_INPROGRESS;
+#else /* LWIP_HTTPD_SUPPORT_REQUESTLIST */
+ return ERR_ARG;
+#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */
+}
+
+#if LWIP_HTTPD_POST_MANUAL_WND
+/** A POST implementation can call this function to update the TCP window.
+ * This can be used to throttle data reception (e.g. when received data is
+ * programmed to flash and data is received faster than programmed).
+ *
+ * @param connection A connection handle passed to httpd_post_begin for which
+ * httpd_post_finished has *NOT* been called yet!
+ * @param recved_len Length of data received (for window update)
+ */
+void httpd_post_data_recved(void *connection, u16_t recved_len)
+{
+ struct http_state *hs = (struct http_state*)connection;
+ if (hs != NULL) {
+ if (hs->no_auto_wnd) {
+ u16_t len = recved_len;
+ if (hs->unrecved_bytes >= recved_len) {
+ hs->unrecved_bytes -= recved_len;
+ } else {
+ LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_LEVEL_WARNING, ("httpd_post_data_recved: recved_len too big\n"));
+ len = (u16_t)hs->unrecved_bytes;
+ hs->unrecved_bytes = 0;
+ }
+ if (hs->pcb != NULL) {
+ if (len != 0) {
+ tcp_recved(hs->pcb, len);
+ }
+ if ((hs->post_content_len_left == 0) && (hs->unrecved_bytes == 0)) {
+ /* finished handling POST */
+ http_handle_post_finished(hs);
+ http_send(hs->pcb, hs);
+ }
+ }
+ }
+ }
+}
+#endif /* LWIP_HTTPD_POST_MANUAL_WND */
+
+#endif /* LWIP_HTTPD_SUPPORT_POST */
+
+#if LWIP_HTTPD_FS_ASYNC_READ
+/** Try to send more data if file has been blocked before
+ * This is a callback function passed to fs_read_async().
+ */
+static void
+http_continue(void *connection)
+{
+ struct http_state *hs = (struct http_state*)connection;
+ if (hs && (hs->pcb) && (hs->handle)) {
+ LWIP_ASSERT("hs->pcb != NULL", hs->pcb != NULL);
+ LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("httpd_continue: try to send more data\n"));
+ if (http_send(hs->pcb, hs)) {
+ /* If we wrote anything to be sent, go ahead and send it now. */
+ LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("tcp_output\n"));
+ tcp_output(hs->pcb);
+ }
+ }
+}
+#endif /* LWIP_HTTPD_FS_ASYNC_READ */
+
+/**
+ * When data has been received in the correct state, try to parse it
+ * as a HTTP request.
+ *
+ * @param inp the received pbuf
+ * @param hs the connection state
+ * @param pcb the tcp_pcb which received this packet
+ * @return ERR_OK if request was OK and hs has been initialized correctly
+ * ERR_INPROGRESS if request was OK so far but not fully received
+ * another err_t otherwise
+ */
+static err_t
+http_parse_request(struct pbuf *inp, struct http_state *hs, struct tcp_pcb *pcb)
+{
+ char *data;
+ char *crlf;
+ u16_t data_len;
+ struct pbuf *p = inp;
+#if LWIP_HTTPD_SUPPORT_REQUESTLIST
+ u16_t clen;
+#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */
+#if LWIP_HTTPD_SUPPORT_POST
+ err_t err;
+#endif /* LWIP_HTTPD_SUPPORT_POST */
+
+ LWIP_UNUSED_ARG(pcb); /* only used for post */
+ LWIP_ASSERT("p != NULL", p != NULL);
+ LWIP_ASSERT("hs != NULL", hs != NULL);
+
+ if ((hs->handle != NULL) || (hs->file != NULL)) {
+ LWIP_DEBUGF(HTTPD_DEBUG, ("Received data while sending a file\n"));
+ /* already sending a file */
+ /* @todo: abort? */
+ return ERR_USE;
+ }
+
+#if LWIP_HTTPD_SUPPORT_REQUESTLIST
+
+ LWIP_DEBUGF(HTTPD_DEBUG, ("Received %"U16_F" bytes\n", p->tot_len));
+
+ /* first check allowed characters in this pbuf? */
+
+ /* enqueue the pbuf */
+ if (hs->req == NULL) {
+ LWIP_DEBUGF(HTTPD_DEBUG, ("First pbuf\n"));
+ hs->req = p;
+ } else {
+ LWIP_DEBUGF(HTTPD_DEBUG, ("pbuf enqueued\n"));
+ pbuf_cat(hs->req, p);
+ }
+ /* increase pbuf ref counter as it is freed when we return but we want to
+ keep it on the req list */
+ pbuf_ref(p);
+
+ if (hs->req->next != NULL) {
+ data_len = LWIP_MIN(hs->req->tot_len, LWIP_HTTPD_MAX_REQ_LENGTH);
+ pbuf_copy_partial(hs->req, httpd_req_buf, data_len, 0);
+ data = httpd_req_buf;
+ } else
+#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */
+ {
+ data = (char *)p->payload;
+ data_len = p->len;
+ if (p->len != p->tot_len) {
+ LWIP_DEBUGF(HTTPD_DEBUG, ("Warning: incomplete header due to chained pbufs\n"));
+ }
+ }
+
+ /* received enough data for minimal request? */
+ if (data_len >= MIN_REQ_LEN) {
+ /* wait for CRLF before parsing anything */
+ crlf = lwip_strnstr(data, CRLF, data_len);
+ if (crlf != NULL) {
+#if LWIP_HTTPD_SUPPORT_POST
+ int is_post = 0;
+#endif /* LWIP_HTTPD_SUPPORT_POST */
+ int is_09 = 0;
+ char *sp1, *sp2;
+ u16_t left_len, uri_len;
+ LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("CRLF received, parsing request\n"));
+ /* parse method */
+ if (!strncmp(data, "GET ", 4)) {
+ sp1 = data + 3;
+ /* received GET request */
+ LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Received GET request\"\n"));
+#if LWIP_HTTPD_SUPPORT_POST
+ } else if (!strncmp(data, "POST ", 5)) {
+ /* store request type */
+ is_post = 1;
+ sp1 = data + 4;
+ /* received GET request */
+ LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Received POST request\n"));
+#endif /* LWIP_HTTPD_SUPPORT_POST */
+ } else {
+ /* null-terminate the METHOD (pbuf is freed anyway wen returning) */
+ data[4] = 0;
+ /* unsupported method! */
+ LWIP_DEBUGF(HTTPD_DEBUG, ("Unsupported request method (not implemented): \"%s\"\n",
+ data));
+ return http_find_error_file(hs, 501);
+ }
+ /* if we come here, method is OK, parse URI */
+ left_len = (u16_t)(data_len - ((sp1 +1) - data));
+ sp2 = lwip_strnstr(sp1 + 1, " ", left_len);
+#if LWIP_HTTPD_SUPPORT_V09
+ if (sp2 == NULL) {
+ /* HTTP 0.9: respond with correct protocol version */
+ sp2 = lwip_strnstr(sp1 + 1, CRLF, left_len);
+ is_09 = 1;
+#if LWIP_HTTPD_SUPPORT_POST
+ if (is_post) {
+ /* HTTP/0.9 does not support POST */
+ goto badrequest;
+ }
+#endif /* LWIP_HTTPD_SUPPORT_POST */
+ }
+#endif /* LWIP_HTTPD_SUPPORT_V09 */
+ uri_len = (u16_t)(sp2 - (sp1 + 1));
+ if ((sp2 != 0) && (sp2 > sp1)) {
+ /* wait for CRLFCRLF (indicating end of HTTP headers) before parsing anything */
+ if (lwip_strnstr(data, CRLF CRLF, data_len) != NULL) {
+ char *uri = sp1 + 1;
+#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE
+ /* This is HTTP/1.0 compatible: for strict 1.1, a connection
+ would always be persistent unless "close" was specified. */
+ if (!is_09 && (lwip_strnstr(data, HTTP11_CONNECTIONKEEPALIVE, data_len) ||
+ lwip_strnstr(data, HTTP11_CONNECTIONKEEPALIVE2, data_len))) {
+ hs->keepalive = 1;
+ } else {
+ hs->keepalive = 0;
+ }
+#endif /* LWIP_HTTPD_SUPPORT_11_KEEPALIVE */
+ /* null-terminate the METHOD (pbuf is freed anyway wen returning) */
+ *sp1 = 0;
+ uri[uri_len] = 0;
+ LWIP_DEBUGF(HTTPD_DEBUG, ("Received \"%s\" request for URI: \"%s\"\n",
+ data, uri));
+#if LWIP_HTTPD_SUPPORT_POST
+ if (is_post) {
+#if LWIP_HTTPD_SUPPORT_REQUESTLIST
+ struct pbuf *q = hs->req;
+#else /* LWIP_HTTPD_SUPPORT_REQUESTLIST */
+ struct pbuf *q = inp;
+#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */
+ err = http_post_request(q, hs, data, data_len, uri, sp2);
+ if (err != ERR_OK) {
+ /* restore header for next try */
+ *sp1 = ' ';
+ *sp2 = ' ';
+ uri[uri_len] = ' ';
+ }
+ if (err == ERR_ARG) {
+ goto badrequest;
+ }
+ return err;
+ } else
+#endif /* LWIP_HTTPD_SUPPORT_POST */
+ {
+ return http_find_file(hs, uri, is_09);
+ }
+ }
+ } else {
+ LWIP_DEBUGF(HTTPD_DEBUG, ("invalid URI\n"));
+ }
+ }
+ }
+
+#if LWIP_HTTPD_SUPPORT_REQUESTLIST
+ clen = pbuf_clen(hs->req);
+ if ((hs->req->tot_len <= LWIP_HTTPD_REQ_BUFSIZE) &&
+ (clen <= LWIP_HTTPD_REQ_QUEUELEN)) {
+ /* request not fully received (too short or CRLF is missing) */
+ return ERR_INPROGRESS;
+ } else
+#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */
+ {
+#if LWIP_HTTPD_SUPPORT_POST
+badrequest:
+#endif /* LWIP_HTTPD_SUPPORT_POST */
+ LWIP_DEBUGF(HTTPD_DEBUG, ("bad request\n"));
+ /* could not parse request */
+ return http_find_error_file(hs, 400);
+ }
+}
+
+/** Try to find the file specified by uri and, if found, initialize hs
+ * accordingly.
+ *
+ * @param hs the connection state
+ * @param uri the HTTP header URI
+ * @param is_09 1 if the request is HTTP/0.9 (no HTTP headers in response)
+ * @return ERR_OK if file was found and hs has been initialized correctly
+ * another err_t otherwise
+ */
+static err_t
+http_find_file(struct http_state *hs, const char *uri, int is_09)
+{
+ size_t loop;
+ struct fs_file *file = NULL;
+ char *params = NULL;
+ err_t err;
+#if LWIP_HTTPD_CGI
+ int i;
+#endif /* LWIP_HTTPD_CGI */
+#if !LWIP_HTTPD_SSI
+ const
+#endif /* !LWIP_HTTPD_SSI */
+ /* By default, assume we will not be processing server-side-includes tags */
+ u8_t tag_check = 0;
+
+ /* Have we been asked for the default file (in root or a directory) ? */
+#if LWIP_HTTPD_MAX_REQUEST_URI_LEN
+ size_t uri_len = strlen(uri);
+ if ((uri_len > 0) && (uri[uri_len-1] == '/') &&
+ ((uri != http_uri_buf) || (uri_len == 1))) {
+ size_t copy_len = LWIP_MIN(sizeof(http_uri_buf) - 1, uri_len - 1);
+ if (copy_len > 0) {
+ MEMCPY(http_uri_buf, uri, copy_len);
+ http_uri_buf[copy_len] = 0;
+ }
+#else /* LWIP_HTTPD_MAX_REQUEST_URI_LEN */
+ if ((uri[0] == '/') && (uri[1] == 0)) {
+#endif /* LWIP_HTTPD_MAX_REQUEST_URI_LEN */
+ /* Try each of the configured default filenames until we find one
+ that exists. */
+ for (loop = 0; loop < NUM_DEFAULT_FILENAMES; loop++) {
+ const char* file_name;
+#if LWIP_HTTPD_MAX_REQUEST_URI_LEN
+ if (copy_len > 0) {
+ size_t len_left = sizeof(http_uri_buf) - copy_len - 1;
+ if (len_left > 0) {
+ size_t name_len = strlen(g_psDefaultFilenames[loop].name);
+ size_t name_copy_len = LWIP_MIN(len_left, name_len);
+ MEMCPY(&http_uri_buf[copy_len], g_psDefaultFilenames[loop].name, name_copy_len);
+ }
+ file_name = http_uri_buf;
+ } else
+#endif /* LWIP_HTTPD_MAX_REQUEST_URI_LEN */
+ {
+ file_name = g_psDefaultFilenames[loop].name;
+ }
+ LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Looking for %s...\n", file_name));
+ err = fs_open(&hs->file_handle, file_name);
+ if(err == ERR_OK) {
+ uri = file_name;
+ file = &hs->file_handle;
+ LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Opened.\n"));
+#if LWIP_HTTPD_SSI
+ tag_check = g_psDefaultFilenames[loop].shtml;
+#endif /* LWIP_HTTPD_SSI */
+ break;
+ }
+ }
+ }
+ if (file == NULL) {
+ /* No - we've been asked for a specific file. */
+ /* First, isolate the base URI (without any parameters) */
+ params = (char *)strchr(uri, '?');
+ if (params != NULL) {
+ /* URI contains parameters. NULL-terminate the base URI */
+ *params = '\0';
+ params++;
+ }
+
+#if LWIP_HTTPD_CGI
+ http_cgi_paramcount = -1;
+ /* Does the base URI we have isolated correspond to a CGI handler? */
+ if (g_iNumCGIs && g_pCGIs) {
+ for (i = 0; i < g_iNumCGIs; i++) {
+ if (strcmp(uri, g_pCGIs[i].pcCGIName) == 0) {
+ /*
+ * We found a CGI that handles this URI so extract the
+ * parameters and call the handler.
+ */
+ http_cgi_paramcount = extract_uri_parameters(hs, params);
+ uri = g_pCGIs[i].pfnCGIHandler(i, http_cgi_paramcount, hs->params,
+ hs->param_vals);
+ break;
+ }
+ }
+ }
+#endif /* LWIP_HTTPD_CGI */
+
+ LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Opening %s\n", uri));
+
+ err = fs_open(&hs->file_handle, uri);
+ if (err == ERR_OK) {
+ file = &hs->file_handle;
+ } else {
+ file = http_get_404_file(hs, &uri);
+ }
+#if LWIP_HTTPD_SSI
+ if (file != NULL) {
+ /* See if we have been asked for an shtml file and, if so,
+ enable tag checking. */
+ const char* ext = NULL, *sub;
+ char* param = (char*)strstr(uri, "?");
+ if (param != NULL) {
+ /* separate uri from parameters for now, set back later */
+ *param = 0;
+ }
+ sub = uri;
+ ext = uri;
+ for (sub = strstr(sub, "."); sub != NULL; sub = strstr(sub, "."))
+ {
+ ext = sub;
+ sub++;
+ }
+ tag_check = 0;
+ for (loop = 0; loop < NUM_SHTML_EXTENSIONS; loop++) {
+ if (!lwip_stricmp(ext, g_pcSSIExtensions[loop])) {
+ tag_check = 1;
+ break;
+ }
+ }
+ if (param != NULL) {
+ *param = '?';
+ }
+ }
+#endif /* LWIP_HTTPD_SSI */
+ }
+ if (file == NULL) {
+ /* None of the default filenames exist so send back a 404 page */
+ file = http_get_404_file(hs, &uri);
+ }
+ return http_init_file(hs, file, is_09, uri, tag_check, params);
+}
+
+/** Initialize a http connection with a file to send (if found).
+ * Called by http_find_file and http_find_error_file.
+ *
+ * @param hs http connection state
+ * @param file file structure to send (or NULL if not found)
+ * @param is_09 1 if the request is HTTP/0.9 (no HTTP headers in response)
+ * @param uri the HTTP header URI
+ * @param tag_check enable SSI tag checking
+ * @param params != NULL if URI has parameters (separated by '?')
+ * @return ERR_OK if file was found and hs has been initialized correctly
+ * another err_t otherwise
+ */
+static err_t
+http_init_file(struct http_state *hs, struct fs_file *file, int is_09, const char *uri,
+ u8_t tag_check, char* params)
+{
+ if (file != NULL) {
+ /* file opened, initialise struct http_state */
+#if LWIP_HTTPD_SSI
+ if (tag_check) {
+ struct http_ssi_state *ssi = http_ssi_state_alloc();
+ if (ssi != NULL) {
+ ssi->tag_index = 0;
+ ssi->tag_state = TAG_NONE;
+ ssi->parsed = file->data;
+ ssi->parse_left = file->len;
+ ssi->tag_end = file->data;
+ hs->ssi = ssi;
+ }
+ }
+#else /* LWIP_HTTPD_SSI */
+ LWIP_UNUSED_ARG(tag_check);
+#endif /* LWIP_HTTPD_SSI */
+ hs->handle = file;
+ hs->file = file->data;
+ LWIP_ASSERT("File length must be positive!", (file->len >= 0));
+#if LWIP_HTTPD_CUSTOM_FILES
+ if (file->is_custom_file && (file->data == NULL)) {
+ /* custom file, need to read data first (via fs_read_custom) */
+ hs->left = 0;
+ } else
+#endif /* LWIP_HTTPD_CUSTOM_FILES */
+ {
+ hs->left = file->len;
+ }
+ hs->retries = 0;
+#if LWIP_HTTPD_TIMING
+ hs->time_started = sys_now();
+#endif /* LWIP_HTTPD_TIMING */
+#if !LWIP_HTTPD_DYNAMIC_HEADERS
+ LWIP_ASSERT("HTTP headers not included in file system",
+ (hs->handle->flags & FS_FILE_FLAGS_HEADER_INCLUDED) != 0);
+#endif /* !LWIP_HTTPD_DYNAMIC_HEADERS */
+#if LWIP_HTTPD_SUPPORT_V09
+ if (is_09 && ((hs->handle->flags & FS_FILE_FLAGS_HEADER_INCLUDED) != 0)) {
+ /* HTTP/0.9 responses are sent without HTTP header,
+ search for the end of the header. */
+ char *file_start = lwip_strnstr(hs->file, CRLF CRLF, hs->left);
+ if (file_start != NULL) {
+ size_t diff = file_start + 4 - hs->file;
+ hs->file += diff;
+ hs->left -= (u32_t)diff;
+ }
+ }
+#endif /* LWIP_HTTPD_SUPPORT_V09*/
+#if LWIP_HTTPD_CGI_SSI
+ if (params != NULL) {
+ /* URI contains parameters, call generic CGI handler */
+ int count;
+#if LWIP_HTTPD_CGI
+ if (http_cgi_paramcount >= 0) {
+ count = http_cgi_paramcount;
+ } else
+#endif
+ {
+ count = extract_uri_parameters(hs, params);
+ }
+ httpd_cgi_handler(uri, count, http_cgi_params, http_cgi_param_vals
+#if defined(LWIP_HTTPD_FILE_STATE) && LWIP_HTTPD_FILE_STATE
+ , hs->handle->state
+#endif /* LWIP_HTTPD_FILE_STATE */
+ );
+ }
+#else /* LWIP_HTTPD_CGI_SSI */
+ LWIP_UNUSED_ARG(params);
+#endif /* LWIP_HTTPD_CGI_SSI */
+ } else {
+ hs->handle = NULL;
+ hs->file = NULL;
+ hs->left = 0;
+ hs->retries = 0;
+ }
+#if LWIP_HTTPD_DYNAMIC_HEADERS
+ /* Determine the HTTP headers to send based on the file extension of
+ * the requested URI. */
+ if ((hs->handle == NULL) || ((hs->handle->flags & FS_FILE_FLAGS_HEADER_INCLUDED) == 0)) {
+ get_http_headers(hs, uri);
+ }
+#else /* LWIP_HTTPD_DYNAMIC_HEADERS */
+ LWIP_UNUSED_ARG(uri);
+#endif /* LWIP_HTTPD_DYNAMIC_HEADERS */
+#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE
+ if (hs->keepalive) {
+#if LWIP_HTTPD_SSI
+ if (hs->ssi != NULL) {
+ hs->keepalive = 0;
+ } else
+#endif /* LWIP_HTTPD_SSI */
+ {
+ if ((hs->handle != NULL) &&
+ ((hs->handle->flags & (FS_FILE_FLAGS_HEADER_INCLUDED|FS_FILE_FLAGS_HEADER_PERSISTENT)) == FS_FILE_FLAGS_HEADER_INCLUDED)) {
+ hs->keepalive = 0;
+ }
+ }
+ }
+#endif /* LWIP_HTTPD_SUPPORT_11_KEEPALIVE */
+ return ERR_OK;
+}
+
+/**
+ * The pcb had an error and is already deallocated.
+ * The argument might still be valid (if != NULL).
+ */
+static void
+http_err(void *arg, err_t err)
+{
+ struct http_state *hs = (struct http_state *)arg;
+ LWIP_UNUSED_ARG(err);
+
+ LWIP_DEBUGF(HTTPD_DEBUG, ("http_err: %s", lwip_strerr(err)));
+
+ if (hs != NULL) {
+ http_state_free(hs);
+ }
+}
+
+/**
+ * Data has been sent and acknowledged by the remote host.
+ * This means that more data can be sent.
+ */
+static err_t
+http_sent(void *arg, struct tcp_pcb *pcb, u16_t len)
+{
+ struct http_state *hs = (struct http_state *)arg;
+
+ LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("http_sent %p\n", (void*)pcb));
+
+ LWIP_UNUSED_ARG(len);
+
+ if (hs == NULL) {
+ return ERR_OK;
+ }
+
+ hs->retries = 0;
+
+ http_send(pcb, hs);
+
+ return ERR_OK;
+}
+
+/**
+ * The poll function is called every 2nd second.
+ * If there has been no data sent (which resets the retries) in 8 seconds, close.
+ * If the last portion of a file has not been sent in 2 seconds, close.
+ *
+ * This could be increased, but we don't want to waste resources for bad connections.
+ */
+static err_t
+http_poll(void *arg, struct tcp_pcb *pcb)
+{
+ struct http_state *hs = (struct http_state *)arg;
+ LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("http_poll: pcb=%p hs=%p pcb_state=%s\n",
+ (void*)pcb, (void*)hs, tcp_debug_state_str(pcb->state)));
+
+ if (hs == NULL) {
+ err_t closed;
+ /* arg is null, close. */
+ LWIP_DEBUGF(HTTPD_DEBUG, ("http_poll: arg is NULL, close\n"));
+ closed = http_close_conn(pcb, NULL);
+ LWIP_UNUSED_ARG(closed);
+#if LWIP_HTTPD_ABORT_ON_CLOSE_MEM_ERROR
+ if (closed == ERR_MEM) {
+ tcp_abort(pcb);
+ return ERR_ABRT;
+ }
+#endif /* LWIP_HTTPD_ABORT_ON_CLOSE_MEM_ERROR */
+ return ERR_OK;
+ } else {
+ hs->retries++;
+ if (hs->retries == HTTPD_MAX_RETRIES) {
+ LWIP_DEBUGF(HTTPD_DEBUG, ("http_poll: too many retries, close\n"));
+ http_close_conn(pcb, hs);
+ return ERR_OK;
+ }
+
+ /* If this connection has a file open, try to send some more data. If
+ * it has not yet received a GET request, don't do this since it will
+ * cause the connection to close immediately. */
+ if(hs && (hs->handle)) {
+ LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("http_poll: try to send more data\n"));
+ if(http_send(pcb, hs)) {
+ /* If we wrote anything to be sent, go ahead and send it now. */
+ LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("tcp_output\n"));
+ tcp_output(pcb);
+ }
+ }
+ }
+
+ return ERR_OK;
+}
+
+/**
+ * Data has been received on this pcb.
+ * For HTTP 1.0, this should normally only happen once (if the request fits in one packet).
+ */
+static err_t
+http_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
+{
+ struct http_state *hs = (struct http_state *)arg;
+ LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("http_recv: pcb=%p pbuf=%p err=%s\n", (void*)pcb,
+ (void*)p, lwip_strerr(err)));
+
+ if ((err != ERR_OK) || (p == NULL) || (hs == NULL)) {
+ /* error or closed by other side? */
+ if (p != NULL) {
+ /* Inform TCP that we have taken the data. */
+ tcp_recved(pcb, p->tot_len);
+ pbuf_free(p);
+ }
+ if (hs == NULL) {
+ /* this should not happen, only to be robust */
+ LWIP_DEBUGF(HTTPD_DEBUG, ("Error, http_recv: hs is NULL, close\n"));
+ }
+ http_close_conn(pcb, hs);
+ return ERR_OK;
+ }
+
+#if LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND
+ if (hs->no_auto_wnd) {
+ hs->unrecved_bytes += p->tot_len;
+ } else
+#endif /* LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND */
+ {
+ /* Inform TCP that we have taken the data. */
+ tcp_recved(pcb, p->tot_len);
+ }
+
+#if LWIP_HTTPD_SUPPORT_POST
+ if (hs->post_content_len_left > 0) {
+ /* reset idle counter when POST data is received */
+ hs->retries = 0;
+ /* this is data for a POST, pass the complete pbuf to the application */
+ http_post_rxpbuf(hs, p);
+ /* pbuf is passed to the application, don't free it! */
+ if (hs->post_content_len_left == 0) {
+ /* all data received, send response or close connection */
+ http_send(pcb, hs);
+ }
+ return ERR_OK;
+ } else
+#endif /* LWIP_HTTPD_SUPPORT_POST */
+ {
+ if (hs->handle == NULL) {
+ err_t parsed = http_parse_request(p, hs, pcb);
+ LWIP_ASSERT("http_parse_request: unexpected return value", parsed == ERR_OK
+ || parsed == ERR_INPROGRESS ||parsed == ERR_ARG || parsed == ERR_USE);
+#if LWIP_HTTPD_SUPPORT_REQUESTLIST
+ if (parsed != ERR_INPROGRESS) {
+ /* request fully parsed or error */
+ if (hs->req != NULL) {
+ pbuf_free(hs->req);
+ hs->req = NULL;
+ }
+ }
+#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */
+ pbuf_free(p);
+ if (parsed == ERR_OK) {
+#if LWIP_HTTPD_SUPPORT_POST
+ if (hs->post_content_len_left == 0)
+#endif /* LWIP_HTTPD_SUPPORT_POST */
+ {
+ LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("http_recv: data %p len %"S32_F"\n", (const void*)hs->file, hs->left));
+ http_send(pcb, hs);
+ }
+ } else if (parsed == ERR_ARG) {
+ /* @todo: close on ERR_USE? */
+ http_close_conn(pcb, hs);
+ }
+ } else {
+ LWIP_DEBUGF(HTTPD_DEBUG, ("http_recv: already sending data\n"));
+ /* already sending but still receiving data, we might want to RST here? */
+ pbuf_free(p);
+ }
+ }
+ return ERR_OK;
+}
+
+/**
+ * A new incoming connection has been accepted.
+ */
+static err_t
+http_accept(void *arg, struct tcp_pcb *pcb, err_t err)
+{
+ struct http_state *hs;
+ LWIP_UNUSED_ARG(err);
+ LWIP_UNUSED_ARG(arg);
+ LWIP_DEBUGF(HTTPD_DEBUG, ("http_accept %p / %p\n", (void*)pcb, arg));
+
+ if ((err != ERR_OK) || (pcb == NULL)) {
+ return ERR_VAL;
+ }
+
+ /* Set priority */
+ tcp_setprio(pcb, HTTPD_TCP_PRIO);
+
+ /* Allocate memory for the structure that holds the state of the
+ connection - initialized by that function. */
+ hs = http_state_alloc();
+ if (hs == NULL) {
+ LWIP_DEBUGF(HTTPD_DEBUG, ("http_accept: Out of memory, RST\n"));
+ return ERR_MEM;
+ }
+ hs->pcb = pcb;
+
+ /* Tell TCP that this is the structure we wish to be passed for our
+ callbacks. */
+ tcp_arg(pcb, hs);
+
+ /* Set up the various callback functions */
+ tcp_recv(pcb, http_recv);
+ tcp_err(pcb, http_err);
+ tcp_poll(pcb, http_poll, HTTPD_POLL_INTERVAL);
+ tcp_sent(pcb, http_sent);
+
+ return ERR_OK;
+}
+
+/**
+ * @ingroup httpd
+ * Initialize the httpd: set up a listening PCB and bind it to the defined port
+ */
+void
+httpd_init(void)
+{
+ struct tcp_pcb *pcb;
+ err_t err;
+
+#if HTTPD_USE_MEM_POOL
+ LWIP_MEMPOOL_INIT(HTTPD_STATE);
+#if LWIP_HTTPD_SSI
+ LWIP_MEMPOOL_INIT(HTTPD_SSI_STATE);
+#endif
+#endif
+ LWIP_DEBUGF(HTTPD_DEBUG, ("httpd_init\n"));
+
+ pcb = tcp_new_ip_type(IPADDR_TYPE_ANY);
+ LWIP_ASSERT("httpd_init: tcp_new failed", pcb != NULL);
+ tcp_setprio(pcb, HTTPD_TCP_PRIO);
+ /* set SOF_REUSEADDR here to explicitly bind httpd to multiple interfaces */
+ err = tcp_bind(pcb, IP_ANY_TYPE, HTTPD_SERVER_PORT);
+ LWIP_UNUSED_ARG(err); /* in case of LWIP_NOASSERT */
+ LWIP_ASSERT("httpd_init: tcp_bind failed", err == ERR_OK);
+ pcb = tcp_listen(pcb);
+ LWIP_ASSERT("httpd_init: tcp_listen failed", pcb != NULL);
+ tcp_accept(pcb, http_accept);
+}
+
+#if LWIP_HTTPD_SSI
+/**
+ * Set the SSI handler function.
+ *
+ * @param ssi_handler the SSI handler function
+ * @param tags an array of SSI tag strings to search for in SSI-enabled files
+ * @param num_tags number of tags in the 'tags' array
+ */
+void
+http_set_ssi_handler(tSSIHandler ssi_handler, const char **tags, int num_tags)
+{
+ LWIP_DEBUGF(HTTPD_DEBUG, ("http_set_ssi_handler\n"));
+
+ LWIP_ASSERT("no ssi_handler given", ssi_handler != NULL);
+ g_pfnSSIHandler = ssi_handler;
+
+#if LWIP_HTTPD_SSI_RAW
+ LWIP_UNUSED_ARG(tags);
+ LWIP_UNUSED_ARG(num_tags);
+#else /* LWIP_HTTPD_SSI_RAW */
+ LWIP_ASSERT("no tags given", tags != NULL);
+ LWIP_ASSERT("invalid number of tags", num_tags > 0);
+
+ g_ppcTags = tags;
+ g_iNumTags = num_tags;
+#endif /* !LWIP_HTTPD_SSI_RAW */
+}
+#endif /* LWIP_HTTPD_SSI */
+
+#if LWIP_HTTPD_CGI
+/**
+ * Set an array of CGI filenames/handler functions
+ *
+ * @param cgis an array of CGI filenames/handler functions
+ * @param num_handlers number of elements in the 'cgis' array
+ */
+void
+http_set_cgi_handlers(const tCGI *cgis, int num_handlers)
+{
+ LWIP_ASSERT("no cgis given", cgis != NULL);
+ LWIP_ASSERT("invalid number of handlers", num_handlers > 0);
+
+ g_pCGIs = cgis;
+ g_iNumCGIs = num_handlers;
+}
+#endif /* LWIP_HTTPD_CGI */
+
+#endif /* LWIP_TCP && LWIP_CALLBACK_API */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/httpd/httpd_structs.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/httpd/httpd_structs.h
new file mode 100644
index 0000000..fbd135a
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/httpd/httpd_structs.h
@@ -0,0 +1,114 @@
+#ifndef LWIP_HTTPD_STRUCTS_H
+#define LWIP_HTTPD_STRUCTS_H
+
+#include "lwip/apps/httpd.h"
+
+#if LWIP_HTTPD_DYNAMIC_HEADERS
+/** This struct is used for a list of HTTP header strings for various
+ * filename extensions. */
+typedef struct
+{
+ const char *extension;
+ const char *content_type;
+} tHTTPHeader;
+
+/** A list of strings used in HTTP headers (see RFC 1945 HTTP/1.0 and
+ * RFC 2616 HTTP/1.1 for header field definitions) */
+static const char * const g_psHTTPHeaderStrings[] =
+{
+ "HTTP/1.0 200 OK\r\n",
+ "HTTP/1.0 404 File not found\r\n",
+ "HTTP/1.0 400 Bad Request\r\n",
+ "HTTP/1.0 501 Not Implemented\r\n",
+ "HTTP/1.1 200 OK\r\n",
+ "HTTP/1.1 404 File not found\r\n",
+ "HTTP/1.1 400 Bad Request\r\n",
+ "HTTP/1.1 501 Not Implemented\r\n",
+ "Content-Length: ",
+ "Connection: Close\r\n",
+ "Connection: keep-alive\r\n",
+ "Connection: keep-alive\r\nContent-Length: ",
+ "Server: "HTTPD_SERVER_AGENT"\r\n",
+ "\r\n<html><body><h2>404: The requested file cannot be found.</h2></body></html>\r\n"
+#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE
+ ,"Connection: keep-alive\r\nContent-Length: 77\r\n\r\n<html><body><h2>404: The requested file cannot be found.</h2></body></html>\r\n"
+#endif
+};
+
+/* Indexes into the g_psHTTPHeaderStrings array */
+#define HTTP_HDR_OK 0 /* 200 OK */
+#define HTTP_HDR_NOT_FOUND 1 /* 404 File not found */
+#define HTTP_HDR_BAD_REQUEST 2 /* 400 Bad request */
+#define HTTP_HDR_NOT_IMPL 3 /* 501 Not Implemented */
+#define HTTP_HDR_OK_11 4 /* 200 OK */
+#define HTTP_HDR_NOT_FOUND_11 5 /* 404 File not found */
+#define HTTP_HDR_BAD_REQUEST_11 6 /* 400 Bad request */
+#define HTTP_HDR_NOT_IMPL_11 7 /* 501 Not Implemented */
+#define HTTP_HDR_CONTENT_LENGTH 8 /* Content-Length: (HTTP 1.0)*/
+#define HTTP_HDR_CONN_CLOSE 9 /* Connection: Close (HTTP 1.1) */
+#define HTTP_HDR_CONN_KEEPALIVE 10 /* Connection: keep-alive (HTTP 1.1) */
+#define HTTP_HDR_KEEPALIVE_LEN 11 /* Connection: keep-alive + Content-Length: (HTTP 1.1)*/
+#define HTTP_HDR_SERVER 12 /* Server: HTTPD_SERVER_AGENT */
+#define DEFAULT_404_HTML 13 /* default 404 body */
+#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE
+#define DEFAULT_404_HTML_PERSISTENT 14 /* default 404 body, but including Connection: keep-alive */
+#endif
+
+
+#define HTTP_HDR_HTML "Content-type: text/html\r\n\r\n"
+#define HTTP_HDR_SSI "Content-type: text/html\r\nExpires: Fri, 10 Apr 2008 14:00:00 GMT\r\nPragma: no-cache\r\n\r\n"
+#define HTTP_HDR_GIF "Content-type: image/gif\r\n\r\n"
+#define HTTP_HDR_PNG "Content-type: image/png\r\n\r\n"
+#define HTTP_HDR_JPG "Content-type: image/jpeg\r\n\r\n"
+#define HTTP_HDR_BMP "Content-type: image/bmp\r\n\r\n"
+#define HTTP_HDR_ICO "Content-type: image/x-icon\r\n\r\n"
+#define HTTP_HDR_APP "Content-type: application/octet-stream\r\n\r\n"
+#define HTTP_HDR_JS "Content-type: application/javascript\r\n\r\n"
+#define HTTP_HDR_RA "Content-type: application/javascript\r\n\r\n"
+#define HTTP_HDR_CSS "Content-type: text/css\r\n\r\n"
+#define HTTP_HDR_SWF "Content-type: application/x-shockwave-flash\r\n\r\n"
+#define HTTP_HDR_XML "Content-type: text/xml\r\n\r\n"
+#define HTTP_HDR_PDF "Content-type: application/pdf\r\n\r\n"
+#define HTTP_HDR_JSON "Content-type: application/json\r\n\r\n"
+
+#define HTTP_HDR_DEFAULT_TYPE "Content-type: text/plain\r\n\r\n"
+
+/** A list of extension-to-HTTP header strings (see outdated RFC 1700 MEDIA TYPES
+ * and http://www.iana.org/assignments/media-types for registered content types
+ * and subtypes) */
+static const tHTTPHeader g_psHTTPHeaders[] =
+{
+ { "html", HTTP_HDR_HTML},
+ { "htm", HTTP_HDR_HTML},
+ { "shtml",HTTP_HDR_SSI},
+ { "shtm", HTTP_HDR_SSI},
+ { "ssi", HTTP_HDR_SSI},
+ { "gif", HTTP_HDR_GIF},
+ { "png", HTTP_HDR_PNG},
+ { "jpg", HTTP_HDR_JPG},
+ { "bmp", HTTP_HDR_BMP},
+ { "ico", HTTP_HDR_ICO},
+ { "class",HTTP_HDR_APP},
+ { "cls", HTTP_HDR_APP},
+ { "js", HTTP_HDR_JS},
+ { "ram", HTTP_HDR_RA},
+ { "css", HTTP_HDR_CSS},
+ { "swf", HTTP_HDR_SWF},
+ { "xml", HTTP_HDR_XML},
+ { "xsl", HTTP_HDR_XML},
+ { "pdf", HTTP_HDR_PDF},
+ { "json", HTTP_HDR_JSON}
+};
+
+#define NUM_HTTP_HEADERS (sizeof(g_psHTTPHeaders) / sizeof(tHTTPHeader))
+
+#endif /* LWIP_HTTPD_DYNAMIC_HEADERS */
+
+#if LWIP_HTTPD_SSI
+static const char * const g_pcSSIExtensions[] = {
+ ".shtml", ".shtm", ".ssi", ".xml"
+};
+#define NUM_SHTML_EXTENSIONS (sizeof(g_pcSSIExtensions) / sizeof(const char *))
+#endif /* LWIP_HTTPD_SSI */
+
+#endif /* LWIP_HTTPD_STRUCTS_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/httpd/makefsdata/makefsdata b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/httpd/makefsdata/makefsdata
new file mode 100644
index 0000000..37b4203
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/httpd/makefsdata/makefsdata
@@ -0,0 +1,97 @@
+#!/usr/bin/perl
+
+open(OUTPUT, "> fsdata.c");
+
+chdir("fs");
+open(FILES, "find . -type f |");
+
+while($file = <FILES>) {
+
+ # Do not include files in CVS directories nor backup files.
+ if($file =~ /(CVS|~)/) {
+ next;
+ }
+
+ chop($file);
+
+ open(HEADER, "> /tmp/header") || die $!;
+ if($file =~ /404/) {
+ print(HEADER "HTTP/1.0 404 File not found\r\n");
+ } else {
+ print(HEADER "HTTP/1.0 200 OK\r\n");
+ }
+ print(HEADER "Server: lwIP/pre-0.6 (http://www.sics.se/~adam/lwip/)\r\n");
+ if($file =~ /\.html$/) {
+ print(HEADER "Content-type: text/html\r\n");
+ } elsif($file =~ /\.gif$/) {
+ print(HEADER "Content-type: image/gif\r\n");
+ } elsif($file =~ /\.png$/) {
+ print(HEADER "Content-type: image/png\r\n");
+ } elsif($file =~ /\.jpg$/) {
+ print(HEADER "Content-type: image/jpeg\r\n");
+ } elsif($file =~ /\.class$/) {
+ print(HEADER "Content-type: application/octet-stream\r\n");
+ } elsif($file =~ /\.ram$/) {
+ print(HEADER "Content-type: audio/x-pn-realaudio\r\n");
+ } else {
+ print(HEADER "Content-type: text/plain\r\n");
+ }
+ print(HEADER "\r\n");
+ close(HEADER);
+
+ unless($file =~ /\.plain$/ || $file =~ /cgi/) {
+ system("cat /tmp/header $file > /tmp/file");
+ } else {
+ system("cp $file /tmp/file");
+ }
+
+ open(FILE, "/tmp/file");
+ unlink("/tmp/file");
+ unlink("/tmp/header");
+
+ $file =~ s/\.//;
+ $fvar = $file;
+ $fvar =~ s-/-_-g;
+ $fvar =~ s-\.-_-g;
+ print(OUTPUT "static const unsigned char data".$fvar."[] = {\n");
+ print(OUTPUT "\t/* $file */\n\t");
+ for($j = 0; $j < length($file); $j++) {
+ printf(OUTPUT "%#02x, ", unpack("C", substr($file, $j, 1)));
+ }
+ printf(OUTPUT "0,\n");
+
+
+ $i = 0;
+ while(read(FILE, $data, 1)) {
+ if($i == 0) {
+ print(OUTPUT "\t");
+ }
+ printf(OUTPUT "%#02x, ", unpack("C", $data));
+ $i++;
+ if($i == 10) {
+ print(OUTPUT "\n");
+ $i = 0;
+ }
+ }
+ print(OUTPUT "};\n\n");
+ close(FILE);
+ push(@fvars, $fvar);
+ push(@files, $file);
+}
+
+for($i = 0; $i < @fvars; $i++) {
+ $file = $files[$i];
+ $fvar = $fvars[$i];
+
+ if($i == 0) {
+ $prevfile = "NULL";
+ } else {
+ $prevfile = "file" . $fvars[$i - 1];
+ }
+ print(OUTPUT "const struct fsdata_file file".$fvar."[] = {{$prevfile, data$fvar, ");
+ print(OUTPUT "data$fvar + ". (length($file) + 1) .", ");
+ print(OUTPUT "sizeof(data$fvar) - ". (length($file) + 1) ."}};\n\n");
+}
+
+print(OUTPUT "#define FS_ROOT file$fvars[$i - 1]\n\n");
+print(OUTPUT "#define FS_NUMFILES $i\n");
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/httpd/makefsdata/makefsdata.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/httpd/makefsdata/makefsdata.c
new file mode 100644
index 0000000..934e721
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/httpd/makefsdata/makefsdata.c
@@ -0,0 +1,1033 @@
+/**
+ * makefsdata: Converts a directory structure for use with the lwIP httpd.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Jim Pettinato
+ * Simon Goldschmidt
+ *
+ * @todo:
+ * - take TCP_MSS, LWIP_TCP_TIMESTAMPS and
+ * PAYLOAD_ALIGN_TYPE/PAYLOAD_ALIGNMENT as arguments
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#ifdef WIN32
+#define WIN32_LEAN_AND_MEAN
+#include "windows.h"
+#else
+#include <dir.h>
+#endif
+#include <dos.h>
+#include <string.h>
+#include <time.h>
+#include <sys/stat.h>
+
+/** Makefsdata can generate *all* files deflate-compressed (where file size shrinks).
+ * Since nearly all browsers support this, this is a good way to reduce ROM size.
+ * To compress the files, "miniz.c" must be downloaded seperately.
+ */
+#ifndef MAKEFS_SUPPORT_DEFLATE
+#define MAKEFS_SUPPORT_DEFLATE 0
+#endif
+
+#define COPY_BUFSIZE (1024*1024) /* 1 MByte */
+
+#if MAKEFS_SUPPORT_DEFLATE
+#include "../miniz.c"
+
+typedef unsigned char uint8;
+typedef unsigned short uint16;
+typedef unsigned int uint;
+
+#define my_max(a,b) (((a) > (b)) ? (a) : (b))
+#define my_min(a,b) (((a) < (b)) ? (a) : (b))
+
+/* COMP_OUT_BUF_SIZE is the size of the output buffer used during compression.
+ COMP_OUT_BUF_SIZE must be >= 1 and <= OUT_BUF_SIZE */
+#define COMP_OUT_BUF_SIZE COPY_BUFSIZE
+
+/* OUT_BUF_SIZE is the size of the output buffer used during decompression.
+ OUT_BUF_SIZE must be a power of 2 >= TINFL_LZ_DICT_SIZE (because the low-level decompressor not only writes, but reads from the output buffer as it decompresses) */
+#define OUT_BUF_SIZE COPY_BUFSIZE
+static uint8 s_outbuf[OUT_BUF_SIZE];
+static uint8 s_checkbuf[OUT_BUF_SIZE];
+
+/* tdefl_compressor contains all the state needed by the low-level compressor so it's a pretty big struct (~300k).
+ This example makes it a global vs. putting it on the stack, of course in real-world usage you'll probably malloc() or new it. */
+tdefl_compressor g_deflator;
+tinfl_decompressor g_inflator;
+
+int deflate_level = 10; /* default compression level, can be changed via command line */
+#define USAGE_ARG_DEFLATE " [-defl<:compr_level>]"
+#else /* MAKEFS_SUPPORT_DEFLATE */
+#define USAGE_ARG_DEFLATE ""
+#endif /* MAKEFS_SUPPORT_DEFLATE */
+
+/* Compatibility defines Win32 vs. DOS */
+#ifdef WIN32
+
+#define FIND_T WIN32_FIND_DATAA
+#define FIND_T_FILENAME(fInfo) (fInfo.cFileName)
+#define FIND_T_IS_DIR(fInfo) ((fInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
+#define FIND_T_IS_FILE(fInfo) ((fInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0)
+#define FIND_RET_T HANDLE
+#define FINDFIRST_FILE(path, result) FindFirstFileA(path, result)
+#define FINDFIRST_DIR(path, result) FindFirstFileA(path, result)
+#define FINDNEXT(ff_res, result) FindNextFileA(ff_res, result)
+#define FINDFIRST_SUCCEEDED(ret) (ret != INVALID_HANDLE_VALUE)
+#define FINDNEXT_SUCCEEDED(ret) (ret == TRUE)
+
+#define GETCWD(path, len) GetCurrentDirectoryA(len, path)
+#define CHDIR(path) SetCurrentDirectoryA(path)
+#define CHDIR_SUCCEEDED(ret) (ret == TRUE)
+
+#else
+
+#define FIND_T struct ffblk
+#define FIND_T_FILENAME(fInfo) (fInfo.ff_name)
+#define FIND_T_IS_DIR(fInfo) ((fInfo.ff_attrib & FA_DIREC) == FA_DIREC)
+#define FIND_T_IS_FILE(fInfo) (1)
+#define FIND_RET_T int
+#define FINDFIRST_FILE(path, result) findfirst(path, result, FA_ARCH)
+#define FINDFIRST_DIR(path, result) findfirst(path, result, FA_DIREC)
+#define FINDNEXT(ff_res, result) FindNextFileA(ff_res, result)
+#define FINDFIRST_SUCCEEDED(ret) (ret == 0)
+#define FINDNEXT_SUCCEEDED(ret) (ret == 0)
+
+#define GETCWD(path, len) getcwd(path, len)
+#define CHDIR(path) chdir(path)
+#define CHDIR_SUCCEEDED(ret) (ret == 0)
+
+#endif
+
+#define NEWLINE "\r\n"
+#define NEWLINE_LEN 2
+
+/* define this to get the header variables we use to build HTTP headers */
+#define LWIP_HTTPD_DYNAMIC_HEADERS 1
+#define LWIP_HTTPD_SSI 1
+#include "lwip/init.h"
+#include "../httpd_structs.h"
+#include "lwip/apps/fs.h"
+
+#include "../core/inet_chksum.c"
+#include "../core/def.c"
+
+/** (Your server name here) */
+const char *serverID = "Server: "HTTPD_SERVER_AGENT"\r\n";
+char serverIDBuffer[1024];
+
+/* change this to suit your MEM_ALIGNMENT */
+#define PAYLOAD_ALIGNMENT 4
+/* set this to 0 to prevent aligning payload */
+#define ALIGN_PAYLOAD 1
+/* define this to a type that has the required alignment */
+#define PAYLOAD_ALIGN_TYPE "unsigned int"
+static int payload_alingment_dummy_counter = 0;
+
+#define HEX_BYTES_PER_LINE 16
+
+#define MAX_PATH_LEN 256
+
+struct file_entry
+{
+ struct file_entry* next;
+ const char* filename_c;
+};
+
+int process_sub(FILE *data_file, FILE *struct_file);
+int process_file(FILE *data_file, FILE *struct_file, const char *filename);
+int file_write_http_header(FILE *data_file, const char *filename, int file_size, u16_t *http_hdr_len,
+ u16_t *http_hdr_chksum, u8_t provide_content_len, int is_compressed);
+int file_put_ascii(FILE *file, const char *ascii_string, int len, int *i);
+int s_put_ascii(char *buf, const char *ascii_string, int len, int *i);
+void concat_files(const char *file1, const char *file2, const char *targetfile);
+int check_path(char* path, size_t size);
+
+/* 5 bytes per char + 3 bytes per line */
+static char file_buffer_c[COPY_BUFSIZE * 5 + ((COPY_BUFSIZE / HEX_BYTES_PER_LINE) * 3)];
+
+char curSubdir[MAX_PATH_LEN];
+char lastFileVar[MAX_PATH_LEN];
+char hdr_buf[4096];
+
+unsigned char processSubs = 1;
+unsigned char includeHttpHeader = 1;
+unsigned char useHttp11 = 0;
+unsigned char supportSsi = 1;
+unsigned char precalcChksum = 0;
+unsigned char includeLastModified = 0;
+#if MAKEFS_SUPPORT_DEFLATE
+unsigned char deflateNonSsiFiles = 0;
+size_t deflatedBytesReduced = 0;
+size_t overallDataBytes = 0;
+#endif
+
+struct file_entry* first_file = NULL;
+struct file_entry* last_file = NULL;
+
+static void print_usage(void)
+{
+ printf(" Usage: htmlgen [targetdir] [-s] [-e] [-i] [-11] [-nossi] [-c] [-f:<filename>] [-m] [-svr:<name>]" USAGE_ARG_DEFLATE NEWLINE NEWLINE);
+ printf(" targetdir: relative or absolute path to files to convert" NEWLINE);
+ printf(" switch -s: toggle processing of subdirectories (default is on)" NEWLINE);
+ printf(" switch -e: exclude HTTP header from file (header is created at runtime, default is off)" NEWLINE);
+ printf(" switch -11: include HTTP 1.1 header (1.0 is default)" NEWLINE);
+ printf(" switch -nossi: no support for SSI (cannot calculate Content-Length for SSI)" NEWLINE);
+ printf(" switch -c: precalculate checksums for all pages (default is off)" NEWLINE);
+ printf(" switch -f: target filename (default is \"fsdata.c\")" NEWLINE);
+ printf(" switch -m: include \"Last-Modified\" header based on file time" NEWLINE);
+ printf(" switch -svr: server identifier sent in HTTP response header ('Server' field)" NEWLINE);
+#if MAKEFS_SUPPORT_DEFLATE
+ printf(" switch -defl: deflate-compress all non-SSI files (with opt. compr.-level, default=10)" NEWLINE);
+ printf(" ATTENTION: browser has to support \"Content-Encoding: deflate\"!" NEWLINE);
+#endif
+ printf(" if targetdir not specified, htmlgen will attempt to" NEWLINE);
+ printf(" process files in subdirectory 'fs'" NEWLINE);
+}
+
+int main(int argc, char *argv[])
+{
+ char path[MAX_PATH_LEN];
+ char appPath[MAX_PATH_LEN];
+ FILE *data_file;
+ FILE *struct_file;
+ int filesProcessed;
+ int i;
+ char targetfile[MAX_PATH_LEN];
+ strcpy(targetfile, "fsdata.c");
+
+ memset(path, 0, sizeof(path));
+ memset(appPath, 0, sizeof(appPath));
+
+ printf(NEWLINE " makefsdata - HTML to C source converter" NEWLINE);
+ printf(" by Jim Pettinato - circa 2003 " NEWLINE);
+ printf(" extended by Simon Goldschmidt - 2009 " NEWLINE NEWLINE);
+
+ strcpy(path, "fs");
+ for (i = 1; i < argc; i++) {
+ if (argv[i] == NULL) {
+ continue;
+ }
+ if (argv[i][0] == '-') {
+ if (strstr(argv[i], "-svr:") == argv[i]) {
+ snprintf(serverIDBuffer, sizeof(serverIDBuffer), "Server: %s\r\n", &argv[i][5]);
+ serverID = serverIDBuffer;
+ printf("Using Server-ID: \"%s\"\n", serverID);
+ } else if (strstr(argv[i], "-s") == argv[i]) {
+ processSubs = 0;
+ } else if (strstr(argv[i], "-e") == argv[i]) {
+ includeHttpHeader = 0;
+ } else if (strstr(argv[i], "-11") == argv[i]) {
+ useHttp11 = 1;
+ } else if (strstr(argv[i], "-nossi") == argv[i]) {
+ supportSsi = 0;
+ } else if (strstr(argv[i], "-c") == argv[i]) {
+ precalcChksum = 1;
+ } else if (strstr(argv[i], "-f:") == argv[i]) {
+ strncpy(targetfile, &argv[i][3], sizeof(targetfile) - 1);
+ targetfile[sizeof(targetfile) - 1] = 0;
+ printf("Writing to file \"%s\"\n", targetfile);
+ } else if (strstr(argv[i], "-m") == argv[i]) {
+ includeLastModified = 1;
+ } else if (strstr(argv[i], "-defl") == argv[i]) {
+#if MAKEFS_SUPPORT_DEFLATE
+ char* colon = strstr(argv[i], ":");
+ if (colon) {
+ if (colon[1] != 0) {
+ int defl_level = atoi(&colon[1]);
+ if ((defl_level >= 0) && (defl_level <= 10)) {
+ deflate_level = defl_level;
+ } else {
+ printf("ERROR: deflate level must be [0..10]" NEWLINE);
+ exit(0);
+ }
+ }
+ }
+ deflateNonSsiFiles = 1;
+ printf("Deflating all non-SSI files with level %d (but only if size is reduced)" NEWLINE, deflate_level);
+#else
+ printf("WARNING: Deflate support is disabled\n");
+#endif
+ } else if ((strstr(argv[i], "-?")) || (strstr(argv[i], "-h"))) {
+ print_usage();
+ exit(0);
+ }
+ } else if ((argv[i][0] == '/') && (argv[i][1] == '?') && (argv[i][2] == 0)) {
+ print_usage();
+ exit(0);
+ } else {
+ strncpy(path, argv[i], sizeof(path)-1);
+ path[sizeof(path)-1] = 0;
+ }
+ }
+
+ if (!check_path(path, sizeof(path))) {
+ printf("Invalid path: \"%s\"." NEWLINE, path);
+ exit(-1);
+ }
+
+ GETCWD(appPath, MAX_PATH_LEN);
+ /* if command line param or subdir named 'fs' not found spout usage verbiage */
+ if (!CHDIR_SUCCEEDED(CHDIR(path))) {
+ /* if no subdir named 'fs' (or the one which was given) exists, spout usage verbiage */
+ printf(" Failed to open directory \"%s\"." NEWLINE NEWLINE, path);
+ print_usage();
+ exit(-1);
+ }
+ CHDIR(appPath);
+
+ printf("HTTP %sheader will %s statically included." NEWLINE,
+ (includeHttpHeader ? (useHttp11 ? "1.1 " : "1.0 ") : ""),
+ (includeHttpHeader ? "be" : "not be"));
+
+ sprintf(curSubdir, ""); /* start off in web page's root directory - relative paths */
+ printf(" Processing all files in directory %s", path);
+ if (processSubs) {
+ printf(" and subdirectories..." NEWLINE NEWLINE);
+ } else {
+ printf("..." NEWLINE NEWLINE);
+ }
+
+ data_file = fopen("fsdata.tmp", "wb");
+ if (data_file == NULL) {
+ printf("Failed to create file \"fsdata.tmp\"\n");
+ exit(-1);
+ }
+ struct_file = fopen("fshdr.tmp", "wb");
+ if (struct_file == NULL) {
+ printf("Failed to create file \"fshdr.tmp\"\n");
+ fclose(data_file);
+ exit(-1);
+ }
+
+ CHDIR(path);
+
+ fprintf(data_file, "#include \"lwip/apps/fs.h\"" NEWLINE);
+ fprintf(data_file, "#include \"lwip/def.h\"" NEWLINE);
+ fprintf(data_file, "#include \"fsdata.h\"" NEWLINE NEWLINE NEWLINE);
+
+ fprintf(data_file, "#define file_NULL (struct fsdata_file *) NULL" NEWLINE NEWLINE NEWLINE);
+ /* define FS_FILE_FLAGS_HEADER_INCLUDED to 1 if not defined (compatibility with older httpd/fs) */
+ fprintf(data_file, "#ifndef FS_FILE_FLAGS_HEADER_INCLUDED" NEWLINE "#define FS_FILE_FLAGS_HEADER_INCLUDED 1" NEWLINE "#endif" NEWLINE);
+ /* define FS_FILE_FLAGS_HEADER_PERSISTENT to 0 if not defined (compatibility with older httpd/fs: wasn't supported back then) */
+ fprintf(data_file, "#ifndef FS_FILE_FLAGS_HEADER_PERSISTENT" NEWLINE "#define FS_FILE_FLAGS_HEADER_PERSISTENT 0" NEWLINE "#endif" NEWLINE);
+
+ /* define alignment defines */
+#if ALIGN_PAYLOAD
+ fprintf(data_file, "/* FSDATA_FILE_ALIGNMENT: 0=off, 1=by variable, 2=by include */" NEWLINE "#ifndef FSDATA_FILE_ALIGNMENT" NEWLINE "#define FSDATA_FILE_ALIGNMENT 0" NEWLINE "#endif" NEWLINE);
+#endif
+ fprintf(data_file, "#ifndef FSDATA_ALIGN_PRE" NEWLINE "#define FSDATA_ALIGN_PRE" NEWLINE "#endif" NEWLINE);
+ fprintf(data_file, "#ifndef FSDATA_ALIGN_POST" NEWLINE "#define FSDATA_ALIGN_POST" NEWLINE "#endif" NEWLINE);
+#if ALIGN_PAYLOAD
+ fprintf(data_file, "#if FSDATA_FILE_ALIGNMENT==2" NEWLINE "#include \"fsdata_alignment.h\"" NEWLINE "#endif" NEWLINE);
+#endif
+
+ sprintf(lastFileVar, "NULL");
+
+ filesProcessed = process_sub(data_file, struct_file);
+
+ /* data_file now contains all of the raw data.. now append linked list of
+ * file header structs to allow embedded app to search for a file name */
+ fprintf(data_file, NEWLINE NEWLINE);
+ fprintf(struct_file, "#define FS_ROOT file_%s" NEWLINE, lastFileVar);
+ fprintf(struct_file, "#define FS_NUMFILES %d" NEWLINE NEWLINE, filesProcessed);
+
+ fclose(data_file);
+ fclose(struct_file);
+
+ CHDIR(appPath);
+ /* append struct_file to data_file */
+ printf(NEWLINE "Creating target file..." NEWLINE NEWLINE);
+ concat_files("fsdata.tmp", "fshdr.tmp", targetfile);
+
+ /* if succeeded, delete the temporary files */
+ if (remove("fsdata.tmp") != 0) {
+ printf("Warning: failed to delete fsdata.tmp\n");
+ }
+ if (remove("fshdr.tmp") != 0) {
+ printf("Warning: failed to delete fshdr.tmp\n");
+ }
+
+ printf(NEWLINE "Processed %d files - done." NEWLINE, filesProcessed);
+#if MAKEFS_SUPPORT_DEFLATE
+ if (deflateNonSsiFiles) {
+ printf("(Deflated total byte reduction: %d bytes -> %d bytes (%.02f%%)" NEWLINE,
+ (int)overallDataBytes, (int)deflatedBytesReduced, (float)((deflatedBytesReduced*100.0)/overallDataBytes));
+ }
+#endif
+ printf(NEWLINE);
+
+ while (first_file != NULL) {
+ struct file_entry* fe = first_file;
+ first_file = fe->next;
+ free(fe);
+ }
+
+ return 0;
+}
+
+int check_path(char* path, size_t size)
+{
+ size_t slen;
+ if (path[0] == 0) {
+ /* empty */
+ return 0;
+ }
+ slen = strlen(path);
+ if (slen >= size) {
+ /* not NULL-terminated */
+ return 0;
+ }
+ while ((slen > 0) && ((path[slen] == '\\') || (path[slen] == '/'))) {
+ /* path should not end with trailing backslash */
+ path[slen] = 0;
+ slen--;
+ }
+ if (slen == 0) {
+ return 0;
+ }
+ return 1;
+}
+
+static void copy_file(const char *filename_in, FILE *fout)
+{
+ FILE *fin;
+ size_t len;
+ void* buf;
+ fin = fopen(filename_in, "rb");
+ if (fin == NULL) {
+ printf("Failed to open file \"%s\"\n", filename_in);
+ exit(-1);
+ }
+ buf = malloc(COPY_BUFSIZE);
+ while ((len = fread(buf, 1, COPY_BUFSIZE, fin)) > 0) {
+ fwrite(buf, 1, len, fout);
+ }
+ free(buf);
+ fclose(fin);
+}
+
+void concat_files(const char *file1, const char *file2, const char *targetfile)
+{
+ FILE *fout;
+ fout = fopen(targetfile, "wb");
+ if (fout == NULL) {
+ printf("Failed to open file \"%s\"\n", targetfile);
+ exit(-1);
+ }
+ copy_file(file1, fout);
+ copy_file(file2, fout);
+ fclose(fout);
+}
+
+int process_sub(FILE *data_file, FILE *struct_file)
+{
+ FIND_T fInfo;
+ FIND_RET_T fret;
+ int filesProcessed = 0;
+
+ if (processSubs) {
+ /* process subs recursively */
+ size_t sublen = strlen(curSubdir);
+ size_t freelen = sizeof(curSubdir) - sublen - 1;
+ LWIP_ASSERT("sublen < sizeof(curSubdir)", sublen < sizeof(curSubdir));
+ fret = FINDFIRST_DIR("*", &fInfo);
+ if (FINDFIRST_SUCCEEDED(fret)) {
+ do {
+ const char *curName = FIND_T_FILENAME(fInfo);
+ if ((curName[0] == '.') || (strcmp(curName, "CVS") == 0)) {
+ continue;
+ }
+ if (!FIND_T_IS_DIR(fInfo)) {
+ continue;
+ }
+ if (freelen > 0) {
+ CHDIR(curName);
+ strncat(curSubdir, "/", freelen);
+ strncat(curSubdir, curName, freelen - 1);
+ curSubdir[sizeof(curSubdir) - 1] = 0;
+ printf("processing subdirectory %s/..." NEWLINE, curSubdir);
+ filesProcessed += process_sub(data_file, struct_file);
+ CHDIR("..");
+ curSubdir[sublen] = 0;
+ } else {
+ printf("WARNING: cannot process sub due to path length restrictions: \"%s/%s\"\n", curSubdir, curName);
+ }
+ } while (FINDNEXT_SUCCEEDED(FINDNEXT(fret, &fInfo)));
+ }
+ }
+
+ fret = FINDFIRST_FILE("*.*", &fInfo);
+ if (FINDFIRST_SUCCEEDED(fret)) {
+ /* at least one file in directory */
+ do {
+ if (FIND_T_IS_FILE(fInfo)) {
+ const char *curName = FIND_T_FILENAME(fInfo);
+ printf("processing %s/%s..." NEWLINE, curSubdir, curName);
+ if (process_file(data_file, struct_file, curName) < 0) {
+ printf(NEWLINE "Error... aborting" NEWLINE);
+ return -1;
+ }
+ filesProcessed++;
+ }
+ } while (FINDNEXT_SUCCEEDED(FINDNEXT(fret, &fInfo)));
+ }
+ return filesProcessed;
+}
+
+u8_t* get_file_data(const char* filename, int* file_size, int can_be_compressed, int* is_compressed)
+{
+ FILE *inFile;
+ size_t fsize = 0;
+ u8_t* buf;
+ size_t r;
+ int rs;
+ inFile = fopen(filename, "rb");
+ if (inFile == NULL) {
+ printf("Failed to open file \"%s\"\n", filename);
+ exit(-1);
+ }
+ fseek(inFile, 0, SEEK_END);
+ rs = ftell(inFile);
+ if (rs < 0) {
+ printf("ftell failed with %d\n", errno);
+ exit(-1);
+ }
+ fsize = (size_t)rs;
+ fseek(inFile, 0, SEEK_SET);
+ buf = (u8_t*)malloc(fsize);
+ LWIP_ASSERT("buf != NULL", buf != NULL);
+ r = fread(buf, 1, fsize, inFile);
+ *file_size = fsize;
+ *is_compressed = 0;
+#if MAKEFS_SUPPORT_DEFLATE
+ overallDataBytes += fsize;
+ if (deflateNonSsiFiles) {
+ if (can_be_compressed) {
+ if (fsize < OUT_BUF_SIZE) {
+ u8_t* ret_buf;
+ tdefl_status status;
+ size_t in_bytes = fsize;
+ size_t out_bytes = OUT_BUF_SIZE;
+ const void *next_in = buf;
+ void *next_out = s_outbuf;
+ /* create tdefl() compatible flags (we have to compose the low-level flags ourselves, or use tdefl_create_comp_flags_from_zip_params() but that means MINIZ_NO_ZLIB_APIS can't be defined). */
+ mz_uint comp_flags = s_tdefl_num_probes[MZ_MIN(10, deflate_level)] | ((deflate_level <= 3) ? TDEFL_GREEDY_PARSING_FLAG : 0);
+ if (!deflate_level) {
+ comp_flags |= TDEFL_FORCE_ALL_RAW_BLOCKS;
+ }
+ status = tdefl_init(&g_deflator, NULL, NULL, comp_flags);
+ if (status != TDEFL_STATUS_OKAY) {
+ printf("tdefl_init() failed!\n");
+ exit(-1);
+ }
+ memset(s_outbuf, 0, sizeof(s_outbuf));
+ status = tdefl_compress(&g_deflator, next_in, &in_bytes, next_out, &out_bytes, TDEFL_FINISH);
+ if (status != TDEFL_STATUS_DONE) {
+ printf("deflate failed: %d\n", status);
+ exit(-1);
+ }
+ LWIP_ASSERT("out_bytes <= COPY_BUFSIZE", out_bytes <= OUT_BUF_SIZE);
+ if (out_bytes < fsize) {
+ ret_buf = (u8_t*)malloc(out_bytes);
+ LWIP_ASSERT("ret_buf != NULL", ret_buf != NULL);
+ memcpy(ret_buf, s_outbuf, out_bytes);
+ {
+ /* sanity-check compression be inflating and comparing to the original */
+ tinfl_status dec_status;
+ tinfl_decompressor inflator;
+ size_t dec_in_bytes = out_bytes;
+ size_t dec_out_bytes = OUT_BUF_SIZE;
+ next_out = s_checkbuf;
+
+ tinfl_init(&inflator);
+ memset(s_checkbuf, 0, sizeof(s_checkbuf));
+ dec_status = tinfl_decompress(&inflator, (const mz_uint8 *)ret_buf, &dec_in_bytes, s_checkbuf, (mz_uint8 *)next_out, &dec_out_bytes, 0);
+ LWIP_ASSERT("tinfl_decompress failed", dec_status == TINFL_STATUS_DONE);
+ LWIP_ASSERT("tinfl_decompress size mismatch", fsize == dec_out_bytes);
+ LWIP_ASSERT("decompressed memcmp failed", !memcmp(s_checkbuf, buf, fsize));
+ }
+ /* free original buffer, use compressed data + size */
+ free(buf);
+ buf = ret_buf;
+ *file_size = out_bytes;
+ printf(" - deflate: %d bytes -> %d bytes (%.02f%%)" NEWLINE, (int)fsize, (int)out_bytes, (float)((out_bytes*100.0)/fsize));
+ deflatedBytesReduced += (size_t)(fsize - out_bytes);
+ *is_compressed = 1;
+ } else {
+ printf(" - uncompressed: (would be %d bytes larger using deflate)" NEWLINE, (int)(out_bytes - fsize));
+ }
+ } else {
+ printf(" - uncompressed: (file is larger than deflate bufer)" NEWLINE);
+ }
+ } else {
+ printf(" - SSI file, cannot be compressed" NEWLINE);
+ }
+ }
+#else
+ LWIP_UNUSED_ARG(can_be_compressed);
+#endif
+ fclose(inFile);
+ return buf;
+}
+
+void process_file_data(FILE* data_file, u8_t* file_data, size_t file_size)
+{
+ size_t written, i, src_off=0;
+
+ size_t off = 0;
+ for (i = 0; i < file_size; i++) {
+ LWIP_ASSERT("file_buffer_c overflow", off < sizeof(file_buffer_c) - 5);
+ sprintf(&file_buffer_c[off], "0x%02.2x,", file_data[i]);
+ off += 5;
+ if ((++src_off % HEX_BYTES_PER_LINE) == 0) {
+ LWIP_ASSERT("file_buffer_c overflow", off < sizeof(file_buffer_c) - NEWLINE_LEN);
+ memcpy(&file_buffer_c[off], NEWLINE, NEWLINE_LEN);
+ off += NEWLINE_LEN;
+ }
+ if (off + 20 >= sizeof(file_buffer_c)) {
+ written = fwrite(file_buffer_c, 1, off, data_file);
+ LWIP_ASSERT("written == off", written == off);
+ off = 0;
+ }
+ }
+ written = fwrite(file_buffer_c, 1, off, data_file);
+ LWIP_ASSERT("written == off", written == off);
+}
+
+int write_checksums(FILE *struct_file, const char *varname,
+ u16_t hdr_len, u16_t hdr_chksum, const u8_t* file_data, size_t file_size)
+{
+ int chunk_size = TCP_MSS;
+ int offset, src_offset;
+ size_t len;
+ int i = 0;
+#if LWIP_TCP_TIMESTAMPS
+ /* when timestamps are used, usable space is 12 bytes less per segment */
+ chunk_size -= 12;
+#endif
+
+ fprintf(struct_file, "#if HTTPD_PRECALCULATED_CHECKSUM" NEWLINE);
+ fprintf(struct_file, "const struct fsdata_chksum chksums_%s[] = {" NEWLINE, varname);
+
+ if (hdr_len > 0) {
+ /* add checksum for HTTP header */
+ fprintf(struct_file, "{%d, 0x%04x, %d}," NEWLINE, 0, hdr_chksum, hdr_len);
+ i++;
+ }
+ src_offset = 0;
+ for (offset = hdr_len; ; offset += len) {
+ unsigned short chksum;
+ void* data = (void*)&file_data[src_offset];
+ len = LWIP_MIN(chunk_size, (int)file_size - src_offset);
+ if (len == 0) {
+ break;
+ }
+ chksum = ~inet_chksum(data, (u16_t)len);
+ /* add checksum for data */
+ fprintf(struct_file, "{%d, 0x%04x, %d}," NEWLINE, offset, chksum, len);
+ i++;
+ }
+ fprintf(struct_file, "};" NEWLINE);
+ fprintf(struct_file, "#endif /* HTTPD_PRECALCULATED_CHECKSUM */" NEWLINE);
+ return i;
+}
+
+static int is_valid_char_for_c_var(char x)
+{
+ if (((x >= 'A') && (x <= 'Z')) ||
+ ((x >= 'a') && (x <= 'z')) ||
+ ((x >= '0') && (x <= '9')) ||
+ (x == '_')) {
+ return 1;
+ }
+ return 0;
+}
+
+static void fix_filename_for_c(char* qualifiedName, size_t max_len)
+{
+ struct file_entry* f;
+ size_t len = strlen(qualifiedName);
+ char *new_name = (char*)malloc(len + 2);
+ int filename_ok;
+ int cnt = 0;
+ size_t i;
+ if (len + 3 == max_len) {
+ printf("File name too long: \"%s\"\n", qualifiedName);
+ exit(-1);
+ }
+ strcpy(new_name, qualifiedName);
+ for (i = 0; i < len; i++) {
+ if (!is_valid_char_for_c_var(new_name[i])) {
+ new_name[i] = '_';
+ }
+ }
+ do {
+ filename_ok = 1;
+ for (f = first_file; f != NULL; f = f->next) {
+ if (!strcmp(f->filename_c, new_name)) {
+ filename_ok = 0;
+ cnt++;
+ /* try next unique file name */
+ sprintf(&new_name[len], "%d", cnt);
+ break;
+ }
+ }
+ } while (!filename_ok && (cnt < 999));
+ if (!filename_ok) {
+ printf("Failed to get unique file name: \"%s\"\n", qualifiedName);
+ exit(-1);
+ }
+ strcpy(qualifiedName, new_name);
+ free(new_name);
+}
+
+static void register_filename(const char* qualifiedName)
+{
+ struct file_entry* fe = (struct file_entry*)malloc(sizeof(struct file_entry));
+ fe->filename_c = strdup(qualifiedName);
+ fe->next = NULL;
+ if (first_file == NULL) {
+ first_file = last_file = fe;
+ } else {
+ last_file->next = fe;
+ last_file = fe;
+ }
+}
+
+int is_ssi_file(const char* filename)
+{
+ size_t loop;
+ for (loop = 0; loop < NUM_SHTML_EXTENSIONS; loop++) {
+ if (strstr(filename, g_pcSSIExtensions[loop])) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+int process_file(FILE *data_file, FILE *struct_file, const char *filename)
+{
+ char varname[MAX_PATH_LEN];
+ int i = 0;
+ char qualifiedName[MAX_PATH_LEN];
+ int file_size;
+ u16_t http_hdr_chksum = 0;
+ u16_t http_hdr_len = 0;
+ int chksum_count = 0;
+ u8_t flags = 0;
+ const char* flags_str;
+ u8_t has_content_len;
+ u8_t* file_data;
+ int is_compressed = 0;
+
+ /* create qualified name (@todo: prepend slash or not?) */
+ sprintf(qualifiedName,"%s/%s", curSubdir, filename);
+ /* create C variable name */
+ strcpy(varname, qualifiedName);
+ /* convert slashes & dots to underscores */
+ fix_filename_for_c(varname, MAX_PATH_LEN);
+ register_filename(varname);
+#if ALIGN_PAYLOAD
+ /* to force even alignment of array, type 1 */
+ fprintf(data_file, "#if FSDATA_FILE_ALIGNMENT==1" NEWLINE);
+ fprintf(data_file, "static const " PAYLOAD_ALIGN_TYPE " dummy_align_%s = %d;" NEWLINE, varname, payload_alingment_dummy_counter++);
+ fprintf(data_file, "#endif" NEWLINE);
+#endif /* ALIGN_PAYLOAD */
+ fprintf(data_file, "static const unsigned char FSDATA_ALIGN_PRE data_%s[] FSDATA_ALIGN_POST = {" NEWLINE, varname);
+ /* encode source file name (used by file system, not returned to browser) */
+ fprintf(data_file, "/* %s (%d chars) */" NEWLINE, qualifiedName, strlen(qualifiedName)+1);
+ file_put_ascii(data_file, qualifiedName, strlen(qualifiedName)+1, &i);
+#if ALIGN_PAYLOAD
+ /* pad to even number of bytes to assure payload is on aligned boundary */
+ while(i % PAYLOAD_ALIGNMENT != 0) {
+ fprintf(data_file, "0x%02.2x,", 0);
+ i++;
+ }
+#endif /* ALIGN_PAYLOAD */
+ fprintf(data_file, NEWLINE);
+
+ has_content_len = !is_ssi_file(filename);
+ file_data = get_file_data(filename, &file_size, includeHttpHeader && has_content_len, &is_compressed);
+ if (includeHttpHeader) {
+ file_write_http_header(data_file, filename, file_size, &http_hdr_len, &http_hdr_chksum, has_content_len, is_compressed);
+ flags = FS_FILE_FLAGS_HEADER_INCLUDED;
+ if (has_content_len) {
+ flags |= FS_FILE_FLAGS_HEADER_PERSISTENT;
+ }
+ }
+ if (precalcChksum) {
+ chksum_count = write_checksums(struct_file, varname, http_hdr_len, http_hdr_chksum, file_data, file_size);
+ }
+
+ /* build declaration of struct fsdata_file in temp file */
+ fprintf(struct_file, "const struct fsdata_file file_%s[] = { {" NEWLINE, varname);
+ fprintf(struct_file, "file_%s," NEWLINE, lastFileVar);
+ fprintf(struct_file, "data_%s," NEWLINE, varname);
+ fprintf(struct_file, "data_%s + %d," NEWLINE, varname, i);
+ fprintf(struct_file, "sizeof(data_%s) - %d," NEWLINE, varname, i);
+ switch(flags)
+ {
+ case(FS_FILE_FLAGS_HEADER_INCLUDED):
+ flags_str = "FS_FILE_FLAGS_HEADER_INCLUDED";
+ break;
+ case(FS_FILE_FLAGS_HEADER_PERSISTENT):
+ flags_str = "FS_FILE_FLAGS_HEADER_PERSISTENT";
+ break;
+ case(FS_FILE_FLAGS_HEADER_INCLUDED | FS_FILE_FLAGS_HEADER_PERSISTENT):
+ flags_str = "FS_FILE_FLAGS_HEADER_INCLUDED | FS_FILE_FLAGS_HEADER_PERSISTENT";
+ break;
+ default:
+ flags_str = "0";
+ break;
+ }
+ fprintf(struct_file, "%s," NEWLINE, flags_str);
+ if (precalcChksum) {
+ fprintf(struct_file, "#if HTTPD_PRECALCULATED_CHECKSUM" NEWLINE);
+ fprintf(struct_file, "%d, chksums_%s," NEWLINE, chksum_count, varname);
+ fprintf(struct_file, "#endif /* HTTPD_PRECALCULATED_CHECKSUM */" NEWLINE);
+ }
+ fprintf(struct_file, "}};" NEWLINE NEWLINE);
+ strcpy(lastFileVar, varname);
+
+ /* write actual file contents */
+ i = 0;
+ fprintf(data_file, NEWLINE "/* raw file data (%d bytes) */" NEWLINE, file_size);
+ process_file_data(data_file, file_data, file_size);
+ fprintf(data_file, "};" NEWLINE NEWLINE);
+ free(file_data);
+ return 0;
+}
+
+int file_write_http_header(FILE *data_file, const char *filename, int file_size, u16_t *http_hdr_len,
+ u16_t *http_hdr_chksum, u8_t provide_content_len, int is_compressed)
+{
+ int i = 0;
+ int response_type = HTTP_HDR_OK;
+ const char* file_type;
+ const char *cur_string;
+ size_t cur_len;
+ int written = 0;
+ size_t hdr_len = 0;
+ u16_t acc;
+ const char *file_ext;
+ int j;
+ u8_t provide_last_modified = includeLastModified;
+
+ memset(hdr_buf, 0, sizeof(hdr_buf));
+
+ if (useHttp11) {
+ response_type = HTTP_HDR_OK_11;
+ }
+
+ fprintf(data_file, NEWLINE "/* HTTP header */");
+ if (strstr(filename, "404") == filename) {
+ response_type = HTTP_HDR_NOT_FOUND;
+ if (useHttp11) {
+ response_type = HTTP_HDR_NOT_FOUND_11;
+ }
+ } else if (strstr(filename, "400") == filename) {
+ response_type = HTTP_HDR_BAD_REQUEST;
+ if (useHttp11) {
+ response_type = HTTP_HDR_BAD_REQUEST_11;
+ }
+ } else if (strstr(filename, "501") == filename) {
+ response_type = HTTP_HDR_NOT_IMPL;
+ if (useHttp11) {
+ response_type = HTTP_HDR_NOT_IMPL_11;
+ }
+ }
+ cur_string = g_psHTTPHeaderStrings[response_type];
+ cur_len = strlen(cur_string);
+ fprintf(data_file, NEWLINE "/* \"%s\" (%d bytes) */" NEWLINE, cur_string, cur_len);
+ written += file_put_ascii(data_file, cur_string, cur_len, &i);
+ i = 0;
+ if (precalcChksum) {
+ memcpy(&hdr_buf[hdr_len], cur_string, cur_len);
+ hdr_len += cur_len;
+ }
+
+ cur_string = serverID;
+ cur_len = strlen(cur_string);
+ fprintf(data_file, NEWLINE "/* \"%s\" (%d bytes) */" NEWLINE, cur_string, cur_len);
+ written += file_put_ascii(data_file, cur_string, cur_len, &i);
+ i = 0;
+ if (precalcChksum) {
+ memcpy(&hdr_buf[hdr_len], cur_string, cur_len);
+ hdr_len += cur_len;
+ }
+
+ file_ext = filename;
+ if (file_ext != NULL) {
+ while(strstr(file_ext, ".") != NULL) {
+ file_ext = strstr(file_ext, ".");
+ file_ext++;
+ }
+ }
+ if ((file_ext == NULL) || (*file_ext == 0)) {
+ printf("failed to get extension for file \"%s\", using default.\n", filename);
+ file_type = HTTP_HDR_DEFAULT_TYPE;
+ } else {
+ file_type = NULL;
+ for (j = 0; j < NUM_HTTP_HEADERS; j++) {
+ if (!strcmp(file_ext, g_psHTTPHeaders[j].extension)) {
+ file_type = g_psHTTPHeaders[j].content_type;
+ break;
+ }
+ }
+ if (file_type == NULL) {
+ printf("failed to get file type for extension \"%s\", using default.\n", file_ext);
+ file_type = HTTP_HDR_DEFAULT_TYPE;
+ }
+ }
+
+ /* Content-Length is used for persistent connections in HTTP/1.1 but also for
+ download progress in older versions
+ @todo: just use a big-enough buffer and let the HTTPD send spaces? */
+ if (provide_content_len) {
+ char intbuf[MAX_PATH_LEN];
+ int content_len = file_size;
+ memset(intbuf, 0, sizeof(intbuf));
+ cur_string = g_psHTTPHeaderStrings[HTTP_HDR_CONTENT_LENGTH];
+ cur_len = strlen(cur_string);
+ fprintf(data_file, NEWLINE "/* \"%s%d\r\n\" (%d+ bytes) */" NEWLINE, cur_string, content_len, cur_len+2);
+ written += file_put_ascii(data_file, cur_string, cur_len, &i);
+ if (precalcChksum) {
+ memcpy(&hdr_buf[hdr_len], cur_string, cur_len);
+ hdr_len += cur_len;
+ }
+
+ _itoa(content_len, intbuf, 10);
+ strcat(intbuf, "\r\n");
+ cur_len = strlen(intbuf);
+ written += file_put_ascii(data_file, intbuf, cur_len, &i);
+ i = 0;
+ if (precalcChksum) {
+ memcpy(&hdr_buf[hdr_len], intbuf, cur_len);
+ hdr_len += cur_len;
+ }
+ }
+ if (provide_last_modified) {
+ char modbuf[256];
+ struct stat stat_data;
+ struct tm* t;
+ memset(modbuf, 0, sizeof(modbuf));
+ memset(&stat_data, 0, sizeof(stat_data));
+ cur_string = modbuf;
+ strcpy(modbuf, "Last-Modified: ");
+ if (stat(filename, &stat_data) != 0) {
+ printf("stat(%s) failed with error %d\n", filename, errno);
+ exit(-1);
+ }
+ t = gmtime(&stat_data.st_mtime);
+ if (t == NULL) {
+ printf("gmtime() failed with error %d\n", errno);
+ exit(-1);
+ }
+ strftime(&modbuf[15], sizeof(modbuf)-15, "%a, %d %b %Y %H:%M:%S GMT", t);
+ cur_len = strlen(cur_string);
+ fprintf(data_file, NEWLINE "/* \"%s\"\r\n\" (%d+ bytes) */" NEWLINE, cur_string, cur_len+2);
+ written += file_put_ascii(data_file, cur_string, cur_len, &i);
+ if (precalcChksum) {
+ memcpy(&hdr_buf[hdr_len], cur_string, cur_len);
+ hdr_len += cur_len;
+ }
+
+ modbuf[0] = 0;
+ strcat(modbuf, "\r\n");
+ cur_len = strlen(modbuf);
+ written += file_put_ascii(data_file, modbuf, cur_len, &i);
+ i = 0;
+ if (precalcChksum) {
+ memcpy(&hdr_buf[hdr_len], modbuf, cur_len);
+ hdr_len += cur_len;
+ }
+ }
+
+ /* HTTP/1.1 implements persistent connections */
+ if (useHttp11) {
+ if (provide_content_len) {
+ cur_string = g_psHTTPHeaderStrings[HTTP_HDR_CONN_KEEPALIVE];
+ } else {
+ /* no Content-Length available, so a persistent connection is no possible
+ because the client does not know the data length */
+ cur_string = g_psHTTPHeaderStrings[HTTP_HDR_CONN_CLOSE];
+ }
+ cur_len = strlen(cur_string);
+ fprintf(data_file, NEWLINE "/* \"%s\" (%d bytes) */" NEWLINE, cur_string, cur_len);
+ written += file_put_ascii(data_file, cur_string, cur_len, &i);
+ i = 0;
+ if (precalcChksum) {
+ memcpy(&hdr_buf[hdr_len], cur_string, cur_len);
+ hdr_len += cur_len;
+ }
+ }
+
+#if MAKEFS_SUPPORT_DEFLATE
+ if (is_compressed) {
+ /* tell the client about the deflate encoding */
+ LWIP_ASSERT("error", deflateNonSsiFiles);
+ cur_string = "Content-Encoding: deflate\r\n";
+ cur_len = strlen(cur_string);
+ fprintf(data_file, NEWLINE "/* \"%s\" (%d bytes) */" NEWLINE, cur_string, cur_len);
+ written += file_put_ascii(data_file, cur_string, cur_len, &i);
+ i = 0;
+ }
+#else
+ LWIP_UNUSED_ARG(is_compressed);
+#endif
+
+ /* write content-type, ATTENTION: this includes the double-CRLF! */
+ cur_string = file_type;
+ cur_len = strlen(cur_string);
+ fprintf(data_file, NEWLINE "/* \"%s\" (%d bytes) */" NEWLINE, cur_string, cur_len);
+ written += file_put_ascii(data_file, cur_string, cur_len, &i);
+ i = 0;
+
+ /* ATTENTION: headers are done now (double-CRLF has been written!) */
+
+ if (precalcChksum) {
+ memcpy(&hdr_buf[hdr_len], cur_string, cur_len);
+ hdr_len += cur_len;
+
+ LWIP_ASSERT("hdr_len <= 0xffff", hdr_len <= 0xffff);
+ LWIP_ASSERT("strlen(hdr_buf) == hdr_len", strlen(hdr_buf) == hdr_len);
+ acc = ~inet_chksum(hdr_buf, (u16_t)hdr_len);
+ *http_hdr_len = (u16_t)hdr_len;
+ *http_hdr_chksum = acc;
+ }
+
+ return written;
+}
+
+int file_put_ascii(FILE *file, const char* ascii_string, int len, int *i)
+{
+ int x;
+ for (x = 0; x < len; x++) {
+ unsigned char cur = ascii_string[x];
+ fprintf(file, "0x%02.2x,", cur);
+ if ((++(*i) % HEX_BYTES_PER_LINE) == 0) {
+ fprintf(file, NEWLINE);
+ }
+ }
+ return len;
+}
+
+int s_put_ascii(char *buf, const char *ascii_string, int len, int *i)
+{
+ int x;
+ int idx = 0;
+ for (x = 0; x < len; x++) {
+ unsigned char cur = ascii_string[x];
+ sprintf(&buf[idx], "0x%02.2x,", cur);
+ idx += 5;
+ if ((++(*i) % HEX_BYTES_PER_LINE) == 0) {
+ sprintf(&buf[idx], NEWLINE);
+ idx += NEWLINE_LEN;
+ }
+ }
+ return len;
+}
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/httpd/makefsdata/readme.txt b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/httpd/makefsdata/readme.txt
new file mode 100644
index 0000000..3768585
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/httpd/makefsdata/readme.txt
@@ -0,0 +1,13 @@
+This directory contains a script ('makefsdata') to create C code suitable for
+httpd for given html pages (or other files) in a directory.
+
+There is also a plain C console application doing the same and extended a bit.
+
+Usage: htmlgen [targetdir] [-s] [-i]s
+ targetdir: relative or absolute path to files to convert
+ switch -s: toggle processing of subdirectories (default is on)
+ switch -e: exclude HTTP header from file (header is created at runtime, default is on)
+ switch -11: include HTTP 1.1 header (1.0 is default)
+
+ if targetdir not specified, makefsdata will attempt to
+ process files in subdirectory 'fs'.
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/lwiperf/lwiperf.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/lwiperf/lwiperf.c
new file mode 100644
index 0000000..efabe47
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/lwiperf/lwiperf.c
@@ -0,0 +1,661 @@
+/**
+ * @file
+ * lwIP iPerf server implementation
+ */
+
+/**
+ * @defgroup iperf Iperf server
+ * @ingroup apps
+ *
+ * This is a simple performance measuring server to check your bandwith using
+ * iPerf2 on a PC as client.
+ * It is currently a minimal implementation providing an IPv4 TCP server only.
+ *
+ * @todo: implement UDP mode and IPv6
+ */
+
+/*
+ * Copyright (c) 2014 Simon Goldschmidt
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Simon Goldschmidt
+ */
+
+#include "lwip/apps/lwiperf.h"
+
+#include "lwip/tcp.h"
+#include "lwip/sys.h"
+
+#include <string.h>
+
+/* Currently, only TCP-over-IPv4 is implemented (does iperf support IPv6 anyway?) */
+#if LWIP_IPV4 && LWIP_TCP && LWIP_CALLBACK_API
+
+/** Specify the idle timeout (in seconds) after that the test fails */
+#ifndef LWIPERF_TCP_MAX_IDLE_SEC
+#define LWIPERF_TCP_MAX_IDLE_SEC 10U
+#endif
+#if LWIPERF_TCP_MAX_IDLE_SEC > 255
+#error LWIPERF_TCP_MAX_IDLE_SEC must fit into an u8_t
+#endif
+
+/* File internal memory allocation (struct lwiperf_*): this defaults to
+ the heap */
+#ifndef LWIPERF_ALLOC
+#define LWIPERF_ALLOC(type) mem_malloc(sizeof(type))
+#define LWIPERF_FREE(type, item) mem_free(item)
+#endif
+
+/** If this is 1, check that received data has the correct format */
+#ifndef LWIPERF_CHECK_RX_DATA
+#define LWIPERF_CHECK_RX_DATA 0
+#endif
+
+/** This is the Iperf settings struct sent from the client */
+typedef struct _lwiperf_settings {
+#define LWIPERF_FLAGS_ANSWER_TEST 0x80000000
+#define LWIPERF_FLAGS_ANSWER_NOW 0x00000001
+ u32_t flags;
+ u32_t num_threads; /* unused for now */
+ u32_t remote_port;
+ u32_t buffer_len; /* unused for now */
+ u32_t win_band; /* TCP window / UDP rate: unused for now */
+ u32_t amount; /* pos. value: bytes?; neg. values: time (unit is 10ms: 1/100 second) */
+} lwiperf_settings_t;
+
+/** Basic connection handle */
+struct _lwiperf_state_base;
+typedef struct _lwiperf_state_base lwiperf_state_base_t;
+struct _lwiperf_state_base {
+ /* 1=tcp, 0=udp */
+ u8_t tcp;
+ /* 1=server, 0=client */
+ u8_t server;
+ lwiperf_state_base_t* next;
+ lwiperf_state_base_t* related_server_state;
+};
+
+/** Connection handle for a TCP iperf session */
+typedef struct _lwiperf_state_tcp {
+ lwiperf_state_base_t base;
+ struct tcp_pcb* server_pcb;
+ struct tcp_pcb* conn_pcb;
+ u32_t time_started;
+ lwiperf_report_fn report_fn;
+ void* report_arg;
+ u8_t poll_count;
+ u8_t next_num;
+ u32_t bytes_transferred;
+ lwiperf_settings_t settings;
+ u8_t have_settings_buf;
+} lwiperf_state_tcp_t;
+
+/** List of active iperf sessions */
+static lwiperf_state_base_t* lwiperf_all_connections;
+/** A const buffer to send from: we want to measure sending, not copying! */
+static const u8_t lwiperf_txbuf_const[1600] = {
+ '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
+ '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
+ '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
+ '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
+ '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
+ '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
+ '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
+ '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
+ '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
+ '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
+ '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
+ '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
+ '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
+ '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
+ '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
+ '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
+ '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
+ '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
+ '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
+ '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
+ '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
+ '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
+ '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
+ '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
+ '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
+ '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
+ '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
+ '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
+ '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
+ '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
+ '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
+ '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
+ '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
+ '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
+ '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
+ '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
+ '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
+ '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
+ '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
+ '0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
+};
+
+static err_t lwiperf_tcp_poll(void *arg, struct tcp_pcb *tpcb);
+static void lwiperf_tcp_err(void *arg, err_t err);
+
+/** Add an iperf session to the 'active' list */
+static void
+lwiperf_list_add(lwiperf_state_base_t* item)
+{
+ if (lwiperf_all_connections == NULL) {
+ lwiperf_all_connections = item;
+ } else {
+ item = lwiperf_all_connections;
+ }
+}
+
+/** Remove an iperf session from the 'active' list */
+static void
+lwiperf_list_remove(lwiperf_state_base_t* item)
+{
+ lwiperf_state_base_t* prev = NULL;
+ lwiperf_state_base_t* iter;
+ for (iter = lwiperf_all_connections; iter != NULL; prev = iter, iter = iter->next) {
+ if (iter == item) {
+ if (prev == NULL) {
+ lwiperf_all_connections = iter->next;
+ } else {
+ prev->next = item;
+ }
+ /* @debug: ensure this item is listed only once */
+ for (iter = iter->next; iter != NULL; iter = iter->next) {
+ LWIP_ASSERT("duplicate entry", iter != item);
+ }
+ break;
+ }
+ }
+}
+
+/** Call the report function of an iperf tcp session */
+static void
+lwip_tcp_conn_report(lwiperf_state_tcp_t* conn, enum lwiperf_report_type report_type)
+{
+ if ((conn != NULL) && (conn->report_fn != NULL)) {
+ u32_t now, duration_ms, bandwidth_kbitpsec;
+ now = sys_now();
+ duration_ms = now - conn->time_started;
+ if (duration_ms == 0) {
+ bandwidth_kbitpsec = 0;
+ } else {
+ bandwidth_kbitpsec = (conn->bytes_transferred / duration_ms) * 8U;
+ }
+ conn->report_fn(conn->report_arg, report_type,
+ &conn->conn_pcb->local_ip, conn->conn_pcb->local_port,
+ &conn->conn_pcb->remote_ip, conn->conn_pcb->remote_port,
+ conn->bytes_transferred, duration_ms, bandwidth_kbitpsec);
+ }
+}
+
+/** Close an iperf tcp session */
+static void
+lwiperf_tcp_close(lwiperf_state_tcp_t* conn, enum lwiperf_report_type report_type)
+{
+ err_t err;
+
+ lwip_tcp_conn_report(conn, report_type);
+ lwiperf_list_remove(&conn->base);
+ if (conn->conn_pcb != NULL) {
+ tcp_arg(conn->conn_pcb, NULL);
+ tcp_poll(conn->conn_pcb, NULL, 0);
+ tcp_sent(conn->conn_pcb, NULL);
+ tcp_recv(conn->conn_pcb, NULL);
+ tcp_err(conn->conn_pcb, NULL);
+ err = tcp_close(conn->conn_pcb);
+ if (err != ERR_OK) {
+ /* don't want to wait for free memory here... */
+ tcp_abort(conn->conn_pcb);
+ }
+ } else {
+ /* no conn pcb, this is the server pcb */
+ err = tcp_close(conn->server_pcb);
+ LWIP_ASSERT("error", err != ERR_OK);
+ }
+ LWIPERF_FREE(lwiperf_state_tcp_t, conn);
+}
+
+/** Try to send more data on an iperf tcp session */
+static err_t
+lwiperf_tcp_client_send_more(lwiperf_state_tcp_t* conn)
+{
+ int send_more;
+ err_t err;
+ u16_t txlen;
+ u16_t txlen_max;
+ void* txptr;
+ u8_t apiflags;
+
+ LWIP_ASSERT("conn invalid", (conn != NULL) && conn->base.tcp && (conn->base.server == 0));
+
+ do {
+ send_more = 0;
+ if (conn->settings.amount & PP_HTONL(0x80000000)) {
+ /* this session is time-limited */
+ u32_t now = sys_now();
+ u32_t diff_ms = now - conn->time_started;
+ u32_t time = (u32_t)-(s32_t)lwip_htonl(conn->settings.amount);
+ u32_t time_ms = time * 10;
+ if (diff_ms >= time_ms) {
+ /* time specified by the client is over -> close the connection */
+ lwiperf_tcp_close(conn, LWIPERF_TCP_DONE_CLIENT);
+ return ERR_OK;
+ }
+ } else {
+ /* this session is byte-limited */
+ u32_t amount_bytes = lwip_htonl(conn->settings.amount);
+ /* @todo: this can send up to 1*MSS more than requested... */
+ if (amount_bytes >= conn->bytes_transferred) {
+ /* all requested bytes transferred -> close the connection */
+ lwiperf_tcp_close(conn, LWIPERF_TCP_DONE_CLIENT);
+ return ERR_OK;
+ }
+ }
+
+ if (conn->bytes_transferred < 24) {
+ /* transmit the settings a first time */
+ txptr = &((u8_t*)&conn->settings)[conn->bytes_transferred];
+ txlen_max = (u16_t)(24 - conn->bytes_transferred);
+ apiflags = TCP_WRITE_FLAG_COPY;
+ } else if (conn->bytes_transferred < 48) {
+ /* transmit the settings a second time */
+ txptr = &((u8_t*)&conn->settings)[conn->bytes_transferred - 24];
+ txlen_max = (u16_t)(48 - conn->bytes_transferred);
+ apiflags = TCP_WRITE_FLAG_COPY | TCP_WRITE_FLAG_MORE;
+ send_more = 1;
+ } else {
+ /* transmit data */
+ /* @todo: every x bytes, transmit the settings again */
+ txptr = LWIP_CONST_CAST(void*, &lwiperf_txbuf_const[conn->bytes_transferred % 10]);
+ txlen_max = TCP_MSS;
+ if (conn->bytes_transferred == 48) { /* @todo: fix this for intermediate settings, too */
+ txlen_max = TCP_MSS - 24;
+ }
+ apiflags = 0; /* no copying needed */
+ send_more = 1;
+ }
+ txlen = txlen_max;
+ do {
+ err = tcp_write(conn->conn_pcb, txptr, txlen, apiflags);
+ if (err == ERR_MEM) {
+ txlen /= 2;
+ }
+ } while ((err == ERR_MEM) && (txlen >= (TCP_MSS/2)));
+
+ if (err == ERR_OK) {
+ conn->bytes_transferred += txlen;
+ } else {
+ send_more = 0;
+ }
+ } while(send_more);
+
+ tcp_output(conn->conn_pcb);
+ return ERR_OK;
+}
+
+/** TCP sent callback, try to send more data */
+static err_t
+lwiperf_tcp_client_sent(void *arg, struct tcp_pcb *tpcb, u16_t len)
+{
+ lwiperf_state_tcp_t* conn = (lwiperf_state_tcp_t*)arg;
+ /* @todo: check 'len' (e.g. to time ACK of all data)? for now, we just send more... */
+ LWIP_ASSERT("invalid conn", conn->conn_pcb == tpcb);
+ LWIP_UNUSED_ARG(tpcb);
+ LWIP_UNUSED_ARG(len);
+
+ conn->poll_count = 0;
+
+ return lwiperf_tcp_client_send_more(conn);
+}
+
+/** TCP connected callback (active connection), send data now */
+static err_t
+lwiperf_tcp_client_connected(void *arg, struct tcp_pcb *tpcb, err_t err)
+{
+ lwiperf_state_tcp_t* conn = (lwiperf_state_tcp_t*)arg;
+ LWIP_ASSERT("invalid conn", conn->conn_pcb == tpcb);
+ LWIP_UNUSED_ARG(tpcb);
+ if (err != ERR_OK) {
+ lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_REMOTE);
+ return ERR_OK;
+ }
+ conn->poll_count = 0;
+ conn->time_started = sys_now();
+ return lwiperf_tcp_client_send_more(conn);
+}
+
+/** Start TCP connection back to the client (either parallel or after the
+ * receive test has finished.
+ */
+static err_t
+lwiperf_tx_start(lwiperf_state_tcp_t* conn)
+{
+ err_t err;
+ lwiperf_state_tcp_t* client_conn;
+ struct tcp_pcb* newpcb;
+ ip_addr_t remote_addr;
+ u16_t remote_port;
+
+ client_conn = (lwiperf_state_tcp_t*)LWIPERF_ALLOC(lwiperf_state_tcp_t);
+ if (client_conn == NULL) {
+ return ERR_MEM;
+ }
+ newpcb = tcp_new();
+ if (newpcb == NULL) {
+ LWIPERF_FREE(lwiperf_state_tcp_t, client_conn);
+ return ERR_MEM;
+ }
+
+ MEMCPY(client_conn, conn, sizeof(lwiperf_state_tcp_t));
+ client_conn->base.server = 0;
+ client_conn->server_pcb = NULL;
+ client_conn->conn_pcb = newpcb;
+ client_conn->time_started = sys_now(); /* @todo: set this again on 'connected' */
+ client_conn->poll_count = 0;
+ client_conn->next_num = 4; /* initial nr is '4' since the header has 24 byte */
+ client_conn->bytes_transferred = 0;
+ client_conn->settings.flags = 0; /* prevent the remote side starting back as client again */
+
+ tcp_arg(newpcb, client_conn);
+ tcp_sent(newpcb, lwiperf_tcp_client_sent);
+ tcp_poll(newpcb, lwiperf_tcp_poll, 2U);
+ tcp_err(newpcb, lwiperf_tcp_err);
+
+ ip_addr_copy(remote_addr, conn->conn_pcb->remote_ip);
+ remote_port = (u16_t)lwip_htonl(client_conn->settings.remote_port);
+
+ err = tcp_connect(newpcb, &remote_addr, remote_port, lwiperf_tcp_client_connected);
+ if (err != ERR_OK) {
+ lwiperf_tcp_close(client_conn, LWIPERF_TCP_ABORTED_LOCAL);
+ return err;
+ }
+ lwiperf_list_add(&client_conn->base);
+ return ERR_OK;
+}
+
+/** Receive data on an iperf tcp session */
+static err_t
+lwiperf_tcp_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
+{
+ u8_t tmp;
+ u16_t tot_len;
+ u32_t packet_idx;
+ struct pbuf* q;
+ lwiperf_state_tcp_t* conn = (lwiperf_state_tcp_t*)arg;
+
+ LWIP_ASSERT("pcb mismatch", conn->conn_pcb == tpcb);
+ LWIP_UNUSED_ARG(tpcb);
+
+ if (err != ERR_OK) {
+ lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_REMOTE);
+ return ERR_OK;
+ }
+ if (p == NULL) {
+ /* connection closed -> test done */
+ if ((conn->settings.flags & PP_HTONL(LWIPERF_FLAGS_ANSWER_TEST|LWIPERF_FLAGS_ANSWER_NOW)) ==
+ PP_HTONL(LWIPERF_FLAGS_ANSWER_TEST)) {
+ /* client requested transmission after end of test */
+ lwiperf_tx_start(conn);
+ }
+ lwiperf_tcp_close(conn, LWIPERF_TCP_DONE_SERVER);
+ return ERR_OK;
+ }
+ tot_len = p->tot_len;
+
+ conn->poll_count = 0;
+
+ if ((!conn->have_settings_buf) || ((conn->bytes_transferred -24) % (1024*128) == 0)) {
+ /* wait for 24-byte header */
+ if (p->tot_len < sizeof(lwiperf_settings_t)) {
+ lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_LOCAL_DATAERROR);
+ pbuf_free(p);
+ return ERR_VAL;
+ }
+ if (!conn->have_settings_buf) {
+ if (pbuf_copy_partial(p, &conn->settings, sizeof(lwiperf_settings_t), 0) != sizeof(lwiperf_settings_t)) {
+ lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_LOCAL);
+ pbuf_free(p);
+ return ERR_VAL;
+ }
+ conn->have_settings_buf = 1;
+ if ((conn->settings.flags & PP_HTONL(LWIPERF_FLAGS_ANSWER_TEST|LWIPERF_FLAGS_ANSWER_NOW)) ==
+ PP_HTONL(LWIPERF_FLAGS_ANSWER_TEST|LWIPERF_FLAGS_ANSWER_NOW)) {
+ /* client requested parallel transmission test */
+ err_t err2 = lwiperf_tx_start(conn);
+ if (err2 != ERR_OK) {
+ lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_LOCAL_TXERROR);
+ pbuf_free(p);
+ return err2;
+ }
+ }
+ } else {
+ if (pbuf_memcmp(p, 0, &conn->settings, sizeof(lwiperf_settings_t)) != 0) {
+ lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_LOCAL_DATAERROR);
+ pbuf_free(p);
+ return ERR_VAL;
+ }
+ }
+ conn->bytes_transferred += sizeof(lwiperf_settings_t);
+ if (conn->bytes_transferred <= 24) {
+ conn->time_started = sys_now();
+ tcp_recved(tpcb, p->tot_len);
+ pbuf_free(p);
+ return ERR_OK;
+ }
+ conn->next_num = 4; /* 24 bytes received... */
+ tmp = pbuf_header(p, -24);
+ LWIP_ASSERT("pbuf_header failed", tmp == 0);
+ }
+
+ packet_idx = 0;
+ for (q = p; q != NULL; q = q->next) {
+#if LWIPERF_CHECK_RX_DATA
+ const u8_t* payload = (const u8_t*)q->payload;
+ u16_t i;
+ for (i = 0; i < q->len; i++) {
+ u8_t val = payload[i];
+ u8_t num = val - '0';
+ if (num == conn->next_num) {
+ conn->next_num++;
+ if (conn->next_num == 10) {
+ conn->next_num = 0;
+ }
+ } else {
+ lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_LOCAL_DATAERROR);
+ pbuf_free(p);
+ return ERR_VAL;
+ }
+ }
+#endif
+ packet_idx += q->len;
+ }
+ LWIP_ASSERT("count mismatch", packet_idx == p->tot_len);
+ conn->bytes_transferred += packet_idx;
+ tcp_recved(tpcb, tot_len);
+ pbuf_free(p);
+ return ERR_OK;
+}
+
+/** Error callback, iperf tcp session aborted */
+static void
+lwiperf_tcp_err(void *arg, err_t err)
+{
+ lwiperf_state_tcp_t* conn = (lwiperf_state_tcp_t*)arg;
+ LWIP_UNUSED_ARG(err);
+ lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_REMOTE);
+}
+
+/** TCP poll callback, try to send more data */
+static err_t
+lwiperf_tcp_poll(void *arg, struct tcp_pcb *tpcb)
+{
+ lwiperf_state_tcp_t* conn = (lwiperf_state_tcp_t*)arg;
+ LWIP_ASSERT("pcb mismatch", conn->conn_pcb == tpcb);
+ LWIP_UNUSED_ARG(tpcb);
+ if (++conn->poll_count >= LWIPERF_TCP_MAX_IDLE_SEC) {
+ lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_LOCAL);
+ return ERR_OK; /* lwiperf_tcp_close frees conn */
+ }
+
+ if (!conn->base.server) {
+ lwiperf_tcp_client_send_more(conn);
+ }
+
+ return ERR_OK;
+}
+
+/** This is called when a new client connects for an iperf tcp session */
+static err_t
+lwiperf_tcp_accept(void *arg, struct tcp_pcb *newpcb, err_t err)
+{
+ lwiperf_state_tcp_t *s, *conn;
+ if ((err != ERR_OK) || (newpcb == NULL) || (arg == NULL)) {
+ return ERR_VAL;
+ }
+
+ s = (lwiperf_state_tcp_t*)arg;
+ conn = (lwiperf_state_tcp_t*)LWIPERF_ALLOC(lwiperf_state_tcp_t);
+ if (conn == NULL) {
+ return ERR_MEM;
+ }
+ memset(conn, 0, sizeof(lwiperf_state_tcp_t));
+ conn->base.tcp = 1;
+ conn->base.server = 1;
+ conn->base.related_server_state = &s->base;
+ conn->server_pcb = s->server_pcb;
+ conn->conn_pcb = newpcb;
+ conn->time_started = sys_now();
+ conn->report_fn = s->report_fn;
+ conn->report_arg = s->report_arg;
+
+ /* setup the tcp rx connection */
+ tcp_arg(newpcb, conn);
+ tcp_recv(newpcb, lwiperf_tcp_recv);
+ tcp_poll(newpcb, lwiperf_tcp_poll, 2U);
+ tcp_err(conn->conn_pcb, lwiperf_tcp_err);
+
+ lwiperf_list_add(&conn->base);
+ return ERR_OK;
+}
+
+/**
+ * @ingroup iperf
+ * Start a TCP iperf server on the default TCP port (5001) and listen for
+ * incoming connections from iperf clients.
+ *
+ * @returns a connection handle that can be used to abort the server
+ * by calling @ref lwiperf_abort()
+ */
+void*
+lwiperf_start_tcp_server_default(lwiperf_report_fn report_fn, void* report_arg)
+{
+ return lwiperf_start_tcp_server(IP_ADDR_ANY, LWIPERF_TCP_PORT_DEFAULT,
+ report_fn, report_arg);
+}
+
+/**
+ * @ingroup iperf
+ * Start a TCP iperf server on a specific IP address and port and listen for
+ * incoming connections from iperf clients.
+ *
+ * @returns a connection handle that can be used to abort the server
+ * by calling @ref lwiperf_abort()
+ */
+void*
+lwiperf_start_tcp_server(const ip_addr_t* local_addr, u16_t local_port,
+ lwiperf_report_fn report_fn, void* report_arg)
+{
+ err_t err;
+ struct tcp_pcb* pcb;
+ lwiperf_state_tcp_t* s;
+
+ if (local_addr == NULL) {
+ return NULL;
+ }
+
+ s = (lwiperf_state_tcp_t*)LWIPERF_ALLOC(lwiperf_state_tcp_t);
+ if (s == NULL) {
+ return NULL;
+ }
+ memset(s, 0, sizeof(lwiperf_state_tcp_t));
+ s->base.tcp = 1;
+ s->base.server = 1;
+ s->report_fn = report_fn;
+ s->report_arg = report_arg;
+
+ pcb = tcp_new();
+ if (pcb != NULL) {
+ err = tcp_bind(pcb, local_addr, local_port);
+ if (err == ERR_OK) {
+ s->server_pcb = tcp_listen_with_backlog(pcb, 1);
+ }
+ }
+ if (s->server_pcb == NULL) {
+ if (pcb != NULL) {
+ tcp_close(pcb);
+ }
+ LWIPERF_FREE(lwiperf_state_tcp_t, s);
+ return NULL;
+ }
+ pcb = NULL;
+
+ tcp_arg(s->server_pcb, s);
+ tcp_accept(s->server_pcb, lwiperf_tcp_accept);
+
+ lwiperf_list_add(&s->base);
+ return s;
+}
+
+/**
+ * @ingroup iperf
+ * Abort an iperf session (handle returned by lwiperf_start_tcp_server*())
+ */
+void
+lwiperf_abort(void* lwiperf_session)
+{
+ lwiperf_state_base_t* i, *dealloc, *last = NULL;
+
+ for (i = lwiperf_all_connections; i != NULL; ) {
+ if ((i == lwiperf_session) || (i->related_server_state == lwiperf_session)) {
+ dealloc = i;
+ i = i->next;
+ if (last != NULL) {
+ last->next = i;
+ }
+ LWIPERF_FREE(lwiperf_state_tcp_t, dealloc); /* @todo: type? */
+ } else {
+ last = i;
+ i = i->next;
+ }
+ }
+}
+
+#endif /* LWIP_IPV4 && LWIP_TCP && LWIP_CALLBACK_API */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/mdns/mdns.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/mdns/mdns.c
new file mode 100644
index 0000000..14334fc
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/mdns/mdns.c
@@ -0,0 +1,2028 @@
+/**
+ * @file
+ * MDNS responder implementation
+ *
+ * @defgroup mdns MDNS
+ * @ingroup apps
+ *
+ * RFC 6762 - Multicast DNS\n
+ * RFC 6763 - DNS-Based Service Discovery\n
+ *
+ * @verbinclude mdns.txt
+ *
+ * Things left to implement:
+ * -------------------------
+ *
+ * - Probing/conflict resolution
+ * - Sending goodbye messages (zero ttl) - shutdown, DHCP lease about to expire, DHCP turned off...
+ * - Checking that source address of unicast requests are on the same network
+ * - Limiting multicast responses to 1 per second per resource record
+ * - Fragmenting replies if required
+ * - Subscribe to netif address/link change events and act on them (currently needs to be done manually)
+ * - Handling multi-packet known answers
+ * - Individual known answer detection for all local IPv6 addresses
+ * - Dynamic size of outgoing packet
+ */
+
+/*
+ * Copyright (c) 2015 Verisure Innovation AB
+ * 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: Erik Ekman <erik@kryo.se>
+ *
+ */
+
+#include "lwip/apps/mdns.h"
+#include "lwip/apps/mdns_priv.h"
+#include "lwip/netif.h"
+#include "lwip/udp.h"
+#include "lwip/ip_addr.h"
+#include "lwip/mem.h"
+#include "lwip/prot/dns.h"
+
+#include <string.h>
+
+#if LWIP_MDNS_RESPONDER
+
+#if (LWIP_IPV4 && !LWIP_IGMP)
+ #error "If you want to use MDNS with IPv4, you have to define LWIP_IGMP=1 in your lwipopts.h"
+#endif
+#if (LWIP_IPV6 && !LWIP_IPV6_MLD)
+#error "If you want to use MDNS with IPv6, you have to define LWIP_IPV6_MLD=1 in your lwipopts.h"
+#endif
+#if (!LWIP_UDP)
+ #error "If you want to use MDNS, you have to define LWIP_UDP=1 in your lwipopts.h"
+#endif
+
+#if LWIP_IPV4
+#include "lwip/igmp.h"
+/* IPv4 multicast group 224.0.0.251 */
+static const ip_addr_t v4group = DNS_MQUERY_IPV4_GROUP_INIT;
+#endif
+
+#if LWIP_IPV6
+#include "lwip/mld6.h"
+/* IPv6 multicast group FF02::FB */
+static const ip_addr_t v6group = DNS_MQUERY_IPV6_GROUP_INIT;
+#endif
+
+#define MDNS_PORT 5353
+#define MDNS_TTL 255
+
+/* Stored offsets to beginning of domain names
+ * Used for compression.
+ */
+#define NUM_DOMAIN_OFFSETS 10
+#define DOMAIN_JUMP_SIZE 2
+#define DOMAIN_JUMP 0xc000
+
+static u8_t mdns_netif_client_id;
+static struct udp_pcb *mdns_pcb;
+
+#define NETIF_TO_HOST(netif) (struct mdns_host*)(netif_get_client_data(netif, mdns_netif_client_id))
+
+#define TOPDOMAIN_LOCAL "local"
+
+#define REVERSE_PTR_TOPDOMAIN "arpa"
+#define REVERSE_PTR_V4_DOMAIN "in-addr"
+#define REVERSE_PTR_V6_DOMAIN "ip6"
+
+#define SRV_PRIORITY 0
+#define SRV_WEIGHT 0
+
+/* Payload size allocated for each outgoing UDP packet */
+#define OUTPACKET_SIZE 500
+
+/* Lookup from hostname -> IPv4 */
+#define REPLY_HOST_A 0x01
+/* Lookup from IPv4/v6 -> hostname */
+#define REPLY_HOST_PTR_V4 0x02
+/* Lookup from hostname -> IPv6 */
+#define REPLY_HOST_AAAA 0x04
+/* Lookup from hostname -> IPv6 */
+#define REPLY_HOST_PTR_V6 0x08
+
+/* Lookup for service types */
+#define REPLY_SERVICE_TYPE_PTR 0x10
+/* Lookup for instances of service */
+#define REPLY_SERVICE_NAME_PTR 0x20
+/* Lookup for location of service instance */
+#define REPLY_SERVICE_SRV 0x40
+/* Lookup for text info on service instance */
+#define REPLY_SERVICE_TXT 0x80
+
+static const char *dnssd_protos[] = {
+ "_udp", /* DNSSD_PROTO_UDP */
+ "_tcp", /* DNSSD_PROTO_TCP */
+};
+
+/** Description of a service */
+struct mdns_service {
+ /** TXT record to answer with */
+ struct mdns_domain txtdata;
+ /** Name of service, like 'myweb' */
+ char name[MDNS_LABEL_MAXLEN + 1];
+ /** Type of service, like '_http' */
+ char service[MDNS_LABEL_MAXLEN + 1];
+ /** Callback function and userdata
+ * to update txtdata buffer */
+ service_get_txt_fn_t txt_fn;
+ void *txt_userdata;
+ /** TTL in seconds of SRV/TXT replies */
+ u32_t dns_ttl;
+ /** Protocol, TCP or UDP */
+ u16_t proto;
+ /** Port of the service */
+ u16_t port;
+};
+
+/** Description of a host/netif */
+struct mdns_host {
+ /** Hostname */
+ char name[MDNS_LABEL_MAXLEN + 1];
+ /** Pointer to services */
+ struct mdns_service *services[MDNS_MAX_SERVICES];
+ /** TTL in seconds of A/AAAA/PTR replies */
+ u32_t dns_ttl;
+};
+
+/** Information about received packet */
+struct mdns_packet {
+ /** Sender IP/port */
+ ip_addr_t source_addr;
+ u16_t source_port;
+ /** If packet was received unicast */
+ u16_t recv_unicast;
+ /** Netif that received the packet */
+ struct netif *netif;
+ /** Packet data */
+ struct pbuf *pbuf;
+ /** Current parsing offset in packet */
+ u16_t parse_offset;
+ /** Identifier. Used in legacy queries */
+ u16_t tx_id;
+ /** Number of questions in packet,
+ * read from packet header */
+ u16_t questions;
+ /** Number of unparsed questions */
+ u16_t questions_left;
+ /** Number of answers in packet,
+ * (sum of normal, authorative and additional answers)
+ * read from packet header */
+ u16_t answers;
+ /** Number of unparsed answers */
+ u16_t answers_left;
+};
+
+/** Information about outgoing packet */
+struct mdns_outpacket {
+ /** Netif to send the packet on */
+ struct netif *netif;
+ /** Packet data */
+ struct pbuf *pbuf;
+ /** Current write offset in packet */
+ u16_t write_offset;
+ /** Identifier. Used in legacy queries */
+ u16_t tx_id;
+ /** Destination IP/port if sent unicast */
+ ip_addr_t dest_addr;
+ u16_t dest_port;
+ /** Number of questions written */
+ u16_t questions;
+ /** Number of normal answers written */
+ u16_t answers;
+ /** Number of additional answers written */
+ u16_t additional;
+ /** Offsets for written domain names in packet.
+ * Used for compression */
+ u16_t domain_offsets[NUM_DOMAIN_OFFSETS];
+ /** If all answers in packet should set cache_flush bit */
+ u8_t cache_flush;
+ /** If reply should be sent unicast */
+ u8_t unicast_reply;
+ /** If legacy query. (tx_id needed, and write
+ * question again in reply before answer) */
+ u8_t legacy_query;
+ /* Reply bitmask for host information */
+ u8_t host_replies;
+ /* Bitmask for which reverse IPv6 hosts to answer */
+ u8_t host_reverse_v6_replies;
+ /* Reply bitmask per service */
+ u8_t serv_replies[MDNS_MAX_SERVICES];
+};
+
+/** Domain, type and class.
+ * Shared between questions and answers */
+struct mdns_rr_info {
+ struct mdns_domain domain;
+ u16_t type;
+ u16_t klass;
+};
+
+struct mdns_question {
+ struct mdns_rr_info info;
+ /** unicast reply requested */
+ u16_t unicast;
+};
+
+struct mdns_answer {
+ struct mdns_rr_info info;
+ /** cache flush command bit */
+ u16_t cache_flush;
+ /* Validity time in seconds */
+ u32_t ttl;
+ /** Length of variable answer */
+ u16_t rd_length;
+ /** Offset of start of variable answer in packet */
+ u16_t rd_offset;
+};
+
+/**
+ * Add a label part to a domain
+ * @param domain The domain to add a label to
+ * @param label The label to add, like &lt;hostname&gt;, 'local', 'com' or ''
+ * @param len The length of the label
+ * @return ERR_OK on success, an err_t otherwise if label too long
+ */
+err_t
+mdns_domain_add_label(struct mdns_domain *domain, const char *label, u8_t len)
+{
+ if (len > MDNS_LABEL_MAXLEN) {
+ return ERR_VAL;
+ }
+ if (len > 0 && (1 + len + domain->length >= MDNS_DOMAIN_MAXLEN)) {
+ return ERR_VAL;
+ }
+ /* Allow only zero marker on last byte */
+ if (len == 0 && (1 + domain->length > MDNS_DOMAIN_MAXLEN)) {
+ return ERR_VAL;
+ }
+ domain->name[domain->length] = len;
+ domain->length++;
+ if (len) {
+ MEMCPY(&domain->name[domain->length], label, len);
+ domain->length += len;
+ }
+ return ERR_OK;
+}
+
+/**
+ * Internal readname function with max 6 levels of recursion following jumps
+ * while decompressing name
+ */
+static u16_t
+mdns_readname_loop(struct pbuf *p, u16_t offset, struct mdns_domain *domain, unsigned depth)
+{
+ u8_t c;
+
+ do {
+ if (depth > 5) {
+ /* Too many jumps */
+ return MDNS_READNAME_ERROR;
+ }
+
+ c = pbuf_get_at(p, offset);
+ offset++;
+
+ /* is this a compressed label? */
+ if((c & 0xc0) == 0xc0) {
+ u16_t jumpaddr;
+ if (offset >= p->tot_len) {
+ /* Make sure both jump bytes fit in the packet */
+ return MDNS_READNAME_ERROR;
+ }
+ jumpaddr = (((c & 0x3f) << 8) | (pbuf_get_at(p, offset) & 0xff));
+ offset++;
+ if (jumpaddr >= SIZEOF_DNS_HDR && jumpaddr < p->tot_len) {
+ u16_t res;
+ /* Recursive call, maximum depth will be checked */
+ res = mdns_readname_loop(p, jumpaddr, domain, depth + 1);
+ /* Dont return offset since new bytes were not read (jumped to somewhere in packet) */
+ if (res == MDNS_READNAME_ERROR) {
+ return res;
+ }
+ } else {
+ return MDNS_READNAME_ERROR;
+ }
+ break;
+ }
+
+ /* normal label */
+ if (c <= MDNS_LABEL_MAXLEN) {
+ u8_t label[MDNS_LABEL_MAXLEN];
+ err_t res;
+
+ if (c + domain->length >= MDNS_DOMAIN_MAXLEN) {
+ return MDNS_READNAME_ERROR;
+ }
+ if (c != 0) {
+ if (pbuf_copy_partial(p, label, c, offset) != c) {
+ return MDNS_READNAME_ERROR;
+ }
+ offset += c;
+ }
+ res = mdns_domain_add_label(domain, (char *) label, c);
+ if (res != ERR_OK) {
+ return MDNS_READNAME_ERROR;
+ }
+ } else {
+ /* bad length byte */
+ return MDNS_READNAME_ERROR;
+ }
+ } while (c != 0);
+
+ return offset;
+}
+
+/**
+ * Read possibly compressed domain name from packet buffer
+ * @param p The packet
+ * @param offset start position of domain name in packet
+ * @param domain The domain name destination
+ * @return The new offset after the domain, or MDNS_READNAME_ERROR
+ * if reading failed
+ */
+u16_t
+mdns_readname(struct pbuf *p, u16_t offset, struct mdns_domain *domain)
+{
+ memset(domain, 0, sizeof(struct mdns_domain));
+ return mdns_readname_loop(p, offset, domain, 0);
+}
+
+/**
+ * Print domain name to debug output
+ * @param domain The domain name
+ */
+static void
+mdns_domain_debug_print(struct mdns_domain *domain)
+{
+ u8_t *src = domain->name;
+ u8_t i;
+
+ while (*src) {
+ u8_t label_len = *src;
+ src++;
+ for (i = 0; i < label_len; i++) {
+ LWIP_DEBUGF(MDNS_DEBUG, ("%c", src[i]));
+ }
+ src += label_len;
+ LWIP_DEBUGF(MDNS_DEBUG, ("."));
+ }
+}
+
+/**
+ * Return 1 if contents of domains match (case-insensitive)
+ * @param a Domain name to compare 1
+ * @param b Domain name to compare 2
+ * @return 1 if domains are equal ignoring case, 0 otherwise
+ */
+int
+mdns_domain_eq(struct mdns_domain *a, struct mdns_domain *b)
+{
+ u8_t *ptra, *ptrb;
+ u8_t len;
+ int res;
+
+ if (a->length != b->length) {
+ return 0;
+ }
+
+ ptra = a->name;
+ ptrb = b->name;
+ while (*ptra && *ptrb && ptra < &a->name[a->length]) {
+ if (*ptra != *ptrb) {
+ return 0;
+ }
+ len = *ptra;
+ ptra++;
+ ptrb++;
+ res = lwip_strnicmp((char *) ptra, (char *) ptrb, len);
+ if (res != 0) {
+ return 0;
+ }
+ ptra += len;
+ ptrb += len;
+ }
+ if (*ptra != *ptrb && ptra < &a->name[a->length]) {
+ return 0;
+ }
+ return 1;
+}
+
+/**
+ * Call user supplied function to setup TXT data
+ * @param service The service to build TXT record for
+ */
+static void
+mdns_prepare_txtdata(struct mdns_service *service)
+{
+ memset(&service->txtdata, 0, sizeof(struct mdns_domain));
+ if (service->txt_fn) {
+ service->txt_fn(service, service->txt_userdata);
+ }
+}
+
+#if LWIP_IPV4
+/**
+ * Build domain for reverse lookup of IPv4 address
+ * like 12.0.168.192.in-addr.arpa. for 192.168.0.12
+ * @param domain Where to write the domain name
+ * @param addr Pointer to an IPv4 address to encode
+ * @return ERR_OK if domain was written, an err_t otherwise
+ */
+static err_t
+mdns_build_reverse_v4_domain(struct mdns_domain *domain, const ip4_addr_t *addr)
+{
+ int i;
+ err_t res;
+ const u8_t *ptr;
+ if (!domain || !addr) {
+ return ERR_ARG;
+ }
+ memset(domain, 0, sizeof(struct mdns_domain));
+ ptr = (const u8_t *) addr;
+ for (i = sizeof(ip4_addr_t) - 1; i >= 0; i--) {
+ char buf[4];
+ u8_t val = ptr[i];
+
+ lwip_itoa(buf, sizeof(buf), val);
+ res = mdns_domain_add_label(domain, buf, (u8_t)strlen(buf));
+ LWIP_ERROR("mdns_build_reverse_v4_domain: Failed to add label", (res == ERR_OK), return res);
+ }
+ res = mdns_domain_add_label(domain, REVERSE_PTR_V4_DOMAIN, (u8_t)(sizeof(REVERSE_PTR_V4_DOMAIN)-1));
+ LWIP_ERROR("mdns_build_reverse_v4_domain: Failed to add label", (res == ERR_OK), return res);
+ res = mdns_domain_add_label(domain, REVERSE_PTR_TOPDOMAIN, (u8_t)(sizeof(REVERSE_PTR_TOPDOMAIN)-1));
+ LWIP_ERROR("mdns_build_reverse_v4_domain: Failed to add label", (res == ERR_OK), return res);
+ res = mdns_domain_add_label(domain, NULL, 0);
+ LWIP_ERROR("mdns_build_reverse_v4_domain: Failed to add label", (res == ERR_OK), return res);
+
+ return ERR_OK;
+}
+#endif
+
+#if LWIP_IPV6
+/**
+ * Build domain for reverse lookup of IP address
+ * like b.a.9.8.7.6.5.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa. for 2001:db8::567:89ab
+ * @param domain Where to write the domain name
+ * @param addr Pointer to an IPv6 address to encode
+ * @return ERR_OK if domain was written, an err_t otherwise
+ */
+static err_t
+mdns_build_reverse_v6_domain(struct mdns_domain *domain, const ip6_addr_t *addr)
+{
+ int i;
+ err_t res;
+ const u8_t *ptr;
+ if (!domain || !addr) {
+ return ERR_ARG;
+ }
+ memset(domain, 0, sizeof(struct mdns_domain));
+ ptr = (const u8_t *) addr;
+ for (i = sizeof(ip6_addr_t) - 1; i >= 0; i--) {
+ char buf;
+ u8_t byte = ptr[i];
+ int j;
+ for (j = 0; j < 2; j++) {
+ if ((byte & 0x0F) < 0xA) {
+ buf = '0' + (byte & 0x0F);
+ } else {
+ buf = 'a' + (byte & 0x0F) - 0xA;
+ }
+ res = mdns_domain_add_label(domain, &buf, sizeof(buf));
+ LWIP_ERROR("mdns_build_reverse_v6_domain: Failed to add label", (res == ERR_OK), return res);
+ byte >>= 4;
+ }
+ }
+ res = mdns_domain_add_label(domain, REVERSE_PTR_V6_DOMAIN, (u8_t)(sizeof(REVERSE_PTR_V6_DOMAIN)-1));
+ LWIP_ERROR("mdns_build_reverse_v6_domain: Failed to add label", (res == ERR_OK), return res);
+ res = mdns_domain_add_label(domain, REVERSE_PTR_TOPDOMAIN, (u8_t)(sizeof(REVERSE_PTR_TOPDOMAIN)-1));
+ LWIP_ERROR("mdns_build_reverse_v6_domain: Failed to add label", (res == ERR_OK), return res);
+ res = mdns_domain_add_label(domain, NULL, 0);
+ LWIP_ERROR("mdns_build_reverse_v6_domain: Failed to add label", (res == ERR_OK), return res);
+
+ return ERR_OK;
+}
+#endif
+
+/* Add .local. to domain */
+static err_t
+mdns_add_dotlocal(struct mdns_domain *domain)
+{
+ err_t res = mdns_domain_add_label(domain, TOPDOMAIN_LOCAL, (u8_t)(sizeof(TOPDOMAIN_LOCAL)-1));
+ LWIP_ERROR("mdns_add_dotlocal: Failed to add label", (res == ERR_OK), return res);
+ return mdns_domain_add_label(domain, NULL, 0);
+}
+
+/**
+ * Build the <hostname>.local. domain name
+ * @param domain Where to write the domain name
+ * @param mdns TMDNS netif descriptor.
+ * @return ERR_OK if domain <hostname>.local. was written, an err_t otherwise
+ */
+static err_t
+mdns_build_host_domain(struct mdns_domain *domain, struct mdns_host *mdns)
+{
+ err_t res;
+ memset(domain, 0, sizeof(struct mdns_domain));
+ LWIP_ERROR("mdns_build_host_domain: mdns != NULL", (mdns != NULL), return ERR_VAL);
+ res = mdns_domain_add_label(domain, mdns->name, (u8_t)strlen(mdns->name));
+ LWIP_ERROR("mdns_build_host_domain: Failed to add label", (res == ERR_OK), return res);
+ return mdns_add_dotlocal(domain);
+}
+
+/**
+ * Build the lookup-all-services special DNS-SD domain name
+ * @param domain Where to write the domain name
+ * @return ERR_OK if domain _services._dns-sd._udp.local. was written, an err_t otherwise
+ */
+static err_t
+mdns_build_dnssd_domain(struct mdns_domain *domain)
+{
+ err_t res;
+ memset(domain, 0, sizeof(struct mdns_domain));
+ res = mdns_domain_add_label(domain, "_services", (u8_t)(sizeof("_services")-1));
+ LWIP_ERROR("mdns_build_dnssd_domain: Failed to add label", (res == ERR_OK), return res);
+ res = mdns_domain_add_label(domain, "_dns-sd", (u8_t)(sizeof("_dns-sd")-1));
+ LWIP_ERROR("mdns_build_dnssd_domain: Failed to add label", (res == ERR_OK), return res);
+ res = mdns_domain_add_label(domain, dnssd_protos[DNSSD_PROTO_UDP], (u8_t)strlen(dnssd_protos[DNSSD_PROTO_UDP]));
+ LWIP_ERROR("mdns_build_dnssd_domain: Failed to add label", (res == ERR_OK), return res);
+ return mdns_add_dotlocal(domain);
+}
+
+/**
+ * Build domain name for a service
+ * @param domain Where to write the domain name
+ * @param service The service struct, containing service name, type and protocol
+ * @param include_name Whether to include the service name in the domain
+ * @return ERR_OK if domain was written. If service name is included,
+ * <name>.<type>.<proto>.local. will be written, otherwise <type>.<proto>.local.
+ * An err_t is returned on error.
+ */
+static err_t
+mdns_build_service_domain(struct mdns_domain *domain, struct mdns_service *service, int include_name)
+{
+ err_t res;
+ memset(domain, 0, sizeof(struct mdns_domain));
+ if (include_name) {
+ res = mdns_domain_add_label(domain, service->name, (u8_t)strlen(service->name));
+ LWIP_ERROR("mdns_build_service_domain: Failed to add label", (res == ERR_OK), return res);
+ }
+ res = mdns_domain_add_label(domain, service->service, (u8_t)strlen(service->service));
+ LWIP_ERROR("mdns_build_service_domain: Failed to add label", (res == ERR_OK), return res);
+ res = mdns_domain_add_label(domain, dnssd_protos[service->proto], (u8_t)strlen(dnssd_protos[service->proto]));
+ LWIP_ERROR("mdns_build_service_domain: Failed to add label", (res == ERR_OK), return res);
+ return mdns_add_dotlocal(domain);
+}
+
+/**
+ * Check which replies we should send for a host/netif based on question
+ * @param netif The network interface that received the question
+ * @param rr Domain/type/class from a question
+ * @param reverse_v6_reply Bitmask of which IPv6 addresses to send reverse PTRs for
+ * if reply bit has REPLY_HOST_PTR_V6 set
+ * @return Bitmask of which replies to send
+ */
+static int
+check_host(struct netif *netif, struct mdns_rr_info *rr, u8_t *reverse_v6_reply)
+{
+ err_t res;
+ int replies = 0;
+ struct mdns_domain mydomain;
+
+ LWIP_UNUSED_ARG(reverse_v6_reply); /* if ipv6 is disabled */
+
+ if (rr->klass != DNS_RRCLASS_IN && rr->klass != DNS_RRCLASS_ANY) {
+ /* Invalid class */
+ return replies;
+ }
+
+ /* Handle PTR for our addresses */
+ if (rr->type == DNS_RRTYPE_PTR || rr->type == DNS_RRTYPE_ANY) {
+#if LWIP_IPV6
+ int i;
+ for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+ if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i))) {
+ res = mdns_build_reverse_v6_domain(&mydomain, netif_ip6_addr(netif, i));
+ if (res == ERR_OK && mdns_domain_eq(&rr->domain, &mydomain)) {
+ replies |= REPLY_HOST_PTR_V6;
+ /* Mark which addresses where requested */
+ if (reverse_v6_reply) {
+ *reverse_v6_reply |= (1 << i);
+ }
+ }
+ }
+ }
+#endif
+#if LWIP_IPV4
+ if (!ip4_addr_isany_val(*netif_ip4_addr(netif))) {
+ res = mdns_build_reverse_v4_domain(&mydomain, netif_ip4_addr(netif));
+ if (res == ERR_OK && mdns_domain_eq(&rr->domain, &mydomain)) {
+ replies |= REPLY_HOST_PTR_V4;
+ }
+ }
+#endif
+ }
+
+ res = mdns_build_host_domain(&mydomain, NETIF_TO_HOST(netif));
+ /* Handle requests for our hostname */
+ if (res == ERR_OK && mdns_domain_eq(&rr->domain, &mydomain)) {
+ /* TODO return NSEC if unsupported protocol requested */
+#if LWIP_IPV4
+ if (!ip4_addr_isany_val(*netif_ip4_addr(netif))
+ && (rr->type == DNS_RRTYPE_A || rr->type == DNS_RRTYPE_ANY)) {
+ replies |= REPLY_HOST_A;
+ }
+#endif
+#if LWIP_IPV6
+ if (rr->type == DNS_RRTYPE_AAAA || rr->type == DNS_RRTYPE_ANY) {
+ replies |= REPLY_HOST_AAAA;
+ }
+#endif
+ }
+
+ return replies;
+}
+
+/**
+ * Check which replies we should send for a service based on question
+ * @param service A registered MDNS service
+ * @param rr Domain/type/class from a question
+ * @return Bitmask of which replies to send
+ */
+static int
+check_service(struct mdns_service *service, struct mdns_rr_info *rr)
+{
+ err_t res;
+ int replies = 0;
+ struct mdns_domain mydomain;
+
+ if (rr->klass != DNS_RRCLASS_IN && rr->klass != DNS_RRCLASS_ANY) {
+ /* Invalid class */
+ return 0;
+ }
+
+ res = mdns_build_dnssd_domain(&mydomain);
+ if (res == ERR_OK && mdns_domain_eq(&rr->domain, &mydomain) &&
+ (rr->type == DNS_RRTYPE_PTR || rr->type == DNS_RRTYPE_ANY)) {
+ /* Request for all service types */
+ replies |= REPLY_SERVICE_TYPE_PTR;
+ }
+
+ res = mdns_build_service_domain(&mydomain, service, 0);
+ if (res == ERR_OK && mdns_domain_eq(&rr->domain, &mydomain) &&
+ (rr->type == DNS_RRTYPE_PTR || rr->type == DNS_RRTYPE_ANY)) {
+ /* Request for the instance of my service */
+ replies |= REPLY_SERVICE_NAME_PTR;
+ }
+
+ res = mdns_build_service_domain(&mydomain, service, 1);
+ if (res == ERR_OK && mdns_domain_eq(&rr->domain, &mydomain)) {
+ /* Request for info about my service */
+ if (rr->type == DNS_RRTYPE_SRV || rr->type == DNS_RRTYPE_ANY) {
+ replies |= REPLY_SERVICE_SRV;
+ }
+ if (rr->type == DNS_RRTYPE_TXT || rr->type == DNS_RRTYPE_ANY) {
+ replies |= REPLY_SERVICE_TXT;
+ }
+ }
+
+ return replies;
+}
+
+/**
+ * Return bytes needed to write before jump for best result of compressing supplied domain
+ * against domain in outpacket starting at specified offset.
+ * If a match is found, offset is updated to where to jump to
+ * @param pbuf Pointer to pbuf with the partially constructed DNS packet
+ * @param offset Start position of a domain written earlier. If this location is suitable
+ * for compression, the pointer is updated to where in the domain to jump to.
+ * @param domain The domain to write
+ * @return Number of bytes to write of the new domain before writing a jump to the offset.
+ * If compression can not be done against this previous domain name, the full new
+ * domain length is returned.
+ */
+u16_t
+mdns_compress_domain(struct pbuf *pbuf, u16_t *offset, struct mdns_domain *domain)
+{
+ struct mdns_domain target;
+ u16_t target_end;
+ u8_t target_len;
+ u8_t writelen = 0;
+ u8_t *ptr;
+ if (pbuf == NULL) {
+ return domain->length;
+ }
+ target_end = mdns_readname(pbuf, *offset, &target);
+ if (target_end == MDNS_READNAME_ERROR) {
+ return domain->length;
+ }
+ target_len = (u8_t)(target_end - *offset);
+ ptr = domain->name;
+ while (writelen < domain->length) {
+ u8_t domainlen = (u8_t)(domain->length - writelen);
+ u8_t labellen;
+ if (domainlen <= target.length && domainlen > DOMAIN_JUMP_SIZE) {
+ /* Compare domains if target is long enough, and we have enough left of the domain */
+ u8_t targetpos = (u8_t)(target.length - domainlen);
+ if ((targetpos + DOMAIN_JUMP_SIZE) >= target_len) {
+ /* We are checking at or beyond a jump in the original, stop looking */
+ break;
+ }
+ if (target.length >= domainlen &&
+ memcmp(&domain->name[writelen], &target.name[targetpos], domainlen) == 0) {
+ *offset += targetpos;
+ return writelen;
+ }
+ }
+ /* Skip to next label in domain */
+ labellen = *ptr;
+ writelen += 1 + labellen;
+ ptr += 1 + labellen;
+ }
+ /* Nothing found */
+ return domain->length;
+}
+
+/**
+ * Write domain to outpacket. Compression will be attempted,
+ * unless domain->skip_compression is set.
+ * @param outpkt The outpacket to write to
+ * @param domain The domain name to write
+ * @return ERR_OK on success, an err_t otherwise
+ */
+static err_t
+mdns_write_domain(struct mdns_outpacket *outpkt, struct mdns_domain *domain)
+{
+ int i;
+ err_t res;
+ u16_t writelen = domain->length;
+ u16_t jump_offset = 0;
+ u16_t jump;
+
+ if (!domain->skip_compression) {
+ for (i = 0; i < NUM_DOMAIN_OFFSETS; ++i) {
+ u16_t offset = outpkt->domain_offsets[i];
+ if (offset) {
+ u16_t len = mdns_compress_domain(outpkt->pbuf, &offset, domain);
+ if (len < writelen) {
+ writelen = len;
+ jump_offset = offset;
+ }
+ }
+ }
+ }
+
+ if (writelen) {
+ /* Write uncompressed part of name */
+ res = pbuf_take_at(outpkt->pbuf, domain->name, writelen, outpkt->write_offset);
+ if (res != ERR_OK) {
+ return res;
+ }
+
+ /* Store offset of this new domain */
+ for (i = 0; i < NUM_DOMAIN_OFFSETS; ++i) {
+ if (outpkt->domain_offsets[i] == 0) {
+ outpkt->domain_offsets[i] = outpkt->write_offset;
+ break;
+ }
+ }
+
+ outpkt->write_offset += writelen;
+ }
+ if (jump_offset) {
+ /* Write jump */
+ jump = lwip_htons(DOMAIN_JUMP | jump_offset);
+ res = pbuf_take_at(outpkt->pbuf, &jump, DOMAIN_JUMP_SIZE, outpkt->write_offset);
+ if (res != ERR_OK) {
+ return res;
+ }
+ outpkt->write_offset += DOMAIN_JUMP_SIZE;
+ }
+ return ERR_OK;
+}
+
+/**
+ * Write a question to an outpacket
+ * A question contains domain, type and class. Since an answer also starts with these fields this function is also
+ * called from mdns_add_answer().
+ * @param outpkt The outpacket to write to
+ * @param domain The domain name the answer is for
+ * @param type The DNS type of the answer (like 'AAAA', 'SRV')
+ * @param klass The DNS type of the answer (like 'IN')
+ * @param unicast If highest bit in class should be set, to instruct the responder to
+ * reply with a unicast packet
+ * @return ERR_OK on success, an err_t otherwise
+ */
+static err_t
+mdns_add_question(struct mdns_outpacket *outpkt, struct mdns_domain *domain, u16_t type, u16_t klass, u16_t unicast)
+{
+ u16_t question_len;
+ u16_t field16;
+ err_t res;
+
+ if (!outpkt->pbuf) {
+ /* If no pbuf is active, allocate one */
+ outpkt->pbuf = pbuf_alloc(PBUF_TRANSPORT, OUTPACKET_SIZE, PBUF_RAM);
+ if (!outpkt->pbuf) {
+ return ERR_MEM;
+ }
+ outpkt->write_offset = SIZEOF_DNS_HDR;
+ }
+
+ /* Worst case calculation. Domain string might be compressed */
+ question_len = domain->length + sizeof(type) + sizeof(klass);
+ if (outpkt->write_offset + question_len > outpkt->pbuf->tot_len) {
+ /* No space */
+ return ERR_MEM;
+ }
+
+ /* Write name */
+ res = mdns_write_domain(outpkt, domain);
+ if (res != ERR_OK) {
+ return res;
+ }
+
+ /* Write type */
+ field16 = lwip_htons(type);
+ res = pbuf_take_at(outpkt->pbuf, &field16, sizeof(field16), outpkt->write_offset);
+ if (res != ERR_OK) {
+ return res;
+ }
+ outpkt->write_offset += sizeof(field16);
+
+ /* Write class */
+ if (unicast) {
+ klass |= 0x8000;
+ }
+ field16 = lwip_htons(klass);
+ res = pbuf_take_at(outpkt->pbuf, &field16, sizeof(field16), outpkt->write_offset);
+ if (res != ERR_OK) {
+ return res;
+ }
+ outpkt->write_offset += sizeof(field16);
+
+ return ERR_OK;
+}
+
+/**
+ * Write answer to reply packet.
+ * buf or answer_domain can be null. The rd_length written will be buf_length +
+ * size of (compressed) domain. Most uses will need either buf or answer_domain,
+ * special case is SRV that starts with 3 u16 and then a domain name.
+ * @param reply The outpacket to write to
+ * @param domain The domain name the answer is for
+ * @param type The DNS type of the answer (like 'AAAA', 'SRV')
+ * @param klass The DNS type of the answer (like 'IN')
+ * @param cache_flush If highest bit in class should be set, to instruct receiver that
+ * this reply replaces any earlier answer for this domain/type/class
+ * @param ttl Validity time in seconds to send out for IP address data in DNS replies
+ * @param buf Pointer to buffer of answer data
+ * @param buf_length Length of variable data
+ * @param answer_domain A domain to write after any buffer data as answer
+ * @return ERR_OK on success, an err_t otherwise
+ */
+static err_t
+mdns_add_answer(struct mdns_outpacket *reply, struct mdns_domain *domain, u16_t type, u16_t klass, u16_t cache_flush,
+ u32_t ttl, const u8_t *buf, size_t buf_length, struct mdns_domain *answer_domain)
+{
+ u16_t answer_len;
+ u16_t field16;
+ u16_t rdlen_offset;
+ u16_t answer_offset;
+ u32_t field32;
+ err_t res;
+
+ if (!reply->pbuf) {
+ /* If no pbuf is active, allocate one */
+ reply->pbuf = pbuf_alloc(PBUF_TRANSPORT, OUTPACKET_SIZE, PBUF_RAM);
+ if (!reply->pbuf) {
+ return ERR_MEM;
+ }
+ reply->write_offset = SIZEOF_DNS_HDR;
+ }
+
+ /* Worst case calculation. Domain strings might be compressed */
+ answer_len = domain->length + sizeof(type) + sizeof(klass) + sizeof(ttl) + sizeof(field16)/*rd_length*/;
+ if (buf) {
+ answer_len += (u16_t)buf_length;
+ }
+ if (answer_domain) {
+ answer_len += answer_domain->length;
+ }
+ if (reply->write_offset + answer_len > reply->pbuf->tot_len) {
+ /* No space */
+ return ERR_MEM;
+ }
+
+ /* Answer starts with same data as question, then more fields */
+ mdns_add_question(reply, domain, type, klass, cache_flush);
+
+ /* Write TTL */
+ field32 = lwip_htonl(ttl);
+ res = pbuf_take_at(reply->pbuf, &field32, sizeof(field32), reply->write_offset);
+ if (res != ERR_OK) {
+ return res;
+ }
+ reply->write_offset += sizeof(field32);
+
+ /* Store offsets and skip forward to the data */
+ rdlen_offset = reply->write_offset;
+ reply->write_offset += sizeof(field16);
+ answer_offset = reply->write_offset;
+
+ if (buf) {
+ /* Write static data */
+ res = pbuf_take_at(reply->pbuf, buf, (u16_t)buf_length, reply->write_offset);
+ if (res != ERR_OK) {
+ return res;
+ }
+ reply->write_offset += (u16_t)buf_length;
+ }
+
+ if (answer_domain) {
+ /* Write name answer (compressed if possible) */
+ res = mdns_write_domain(reply, answer_domain);
+ if (res != ERR_OK) {
+ return res;
+ }
+ }
+
+ /* Write rd_length after when we know the answer size */
+ field16 = lwip_htons(reply->write_offset - answer_offset);
+ res = pbuf_take_at(reply->pbuf, &field16, sizeof(field16), rdlen_offset);
+
+ return res;
+}
+
+/**
+ * Helper function for mdns_read_question/mdns_read_answer
+ * Reads a domain, type and class from the packet
+ * @param pkt The MDNS packet to read from. The parse_offset field will be
+ * incremented to point to the next unparsed byte.
+ * @param info The struct to fill with domain, type and class
+ * @return ERR_OK on success, an err_t otherwise
+ */
+static err_t
+mdns_read_rr_info(struct mdns_packet *pkt, struct mdns_rr_info *info)
+{
+ u16_t field16, copied;
+ pkt->parse_offset = mdns_readname(pkt->pbuf, pkt->parse_offset, &info->domain);
+ if (pkt->parse_offset == MDNS_READNAME_ERROR) {
+ return ERR_VAL;
+ }
+
+ copied = pbuf_copy_partial(pkt->pbuf, &field16, sizeof(field16), pkt->parse_offset);
+ if (copied != sizeof(field16)) {
+ return ERR_VAL;
+ }
+ pkt->parse_offset += copied;
+ info->type = lwip_ntohs(field16);
+
+ copied = pbuf_copy_partial(pkt->pbuf, &field16, sizeof(field16), pkt->parse_offset);
+ if (copied != sizeof(field16)) {
+ return ERR_VAL;
+ }
+ pkt->parse_offset += copied;
+ info->klass = lwip_ntohs(field16);
+
+ return ERR_OK;
+}
+
+/**
+ * Read a question from the packet.
+ * All questions have to be read before the answers.
+ * @param pkt The MDNS packet to read from. The questions_left field will be decremented
+ * and the parse_offset will be updated.
+ * @param question The struct to fill with question data
+ * @return ERR_OK on success, an err_t otherwise
+ */
+static err_t
+mdns_read_question(struct mdns_packet *pkt, struct mdns_question *question)
+{
+ /* Safety check */
+ if (pkt->pbuf->tot_len < pkt->parse_offset) {
+ return ERR_VAL;
+ }
+
+ if (pkt->questions_left) {
+ err_t res;
+ pkt->questions_left--;
+
+ memset(question, 0, sizeof(struct mdns_question));
+ res = mdns_read_rr_info(pkt, &question->info);
+ if (res != ERR_OK) {
+ return res;
+ }
+
+ /* Extract unicast flag from class field */
+ question->unicast = question->info.klass & 0x8000;
+ question->info.klass &= 0x7FFF;
+
+ return ERR_OK;
+ }
+ return ERR_VAL;
+}
+
+/**
+ * Read an answer from the packet
+ * The variable length reply is not copied, its pbuf offset and length is stored instead.
+ * @param pkt The MDNS packet to read. The answers_left field will be decremented and
+ * the parse_offset will be updated.
+ * @param answer The struct to fill with answer data
+ * @return ERR_OK on success, an err_t otherwise
+ */
+static err_t
+mdns_read_answer(struct mdns_packet *pkt, struct mdns_answer *answer)
+{
+ /* Read questions first */
+ if (pkt->questions_left) {
+ return ERR_VAL;
+ }
+
+ /* Safety check */
+ if (pkt->pbuf->tot_len < pkt->parse_offset) {
+ return ERR_VAL;
+ }
+
+ if (pkt->answers_left) {
+ u16_t copied, field16;
+ u32_t ttl;
+ err_t res;
+ pkt->answers_left--;
+
+ memset(answer, 0, sizeof(struct mdns_answer));
+ res = mdns_read_rr_info(pkt, &answer->info);
+ if (res != ERR_OK) {
+ return res;
+ }
+
+ /* Extract cache_flush flag from class field */
+ answer->cache_flush = answer->info.klass & 0x8000;
+ answer->info.klass &= 0x7FFF;
+
+ copied = pbuf_copy_partial(pkt->pbuf, &ttl, sizeof(ttl), pkt->parse_offset);
+ if (copied != sizeof(ttl)) {
+ return ERR_VAL;
+ }
+ pkt->parse_offset += copied;
+ answer->ttl = lwip_ntohl(ttl);
+
+ copied = pbuf_copy_partial(pkt->pbuf, &field16, sizeof(field16), pkt->parse_offset);
+ if (copied != sizeof(field16)) {
+ return ERR_VAL;
+ }
+ pkt->parse_offset += copied;
+ answer->rd_length = lwip_ntohs(field16);
+
+ answer->rd_offset = pkt->parse_offset;
+ pkt->parse_offset += answer->rd_length;
+
+ return ERR_OK;
+ }
+ return ERR_VAL;
+}
+
+#if LWIP_IPV4
+/** Write an IPv4 address (A) RR to outpacket */
+static err_t
+mdns_add_a_answer(struct mdns_outpacket *reply, u16_t cache_flush, struct netif *netif)
+{
+ struct mdns_domain host;
+ mdns_build_host_domain(&host, NETIF_TO_HOST(netif));
+ LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with A record\n"));
+ return mdns_add_answer(reply, &host, DNS_RRTYPE_A, DNS_RRCLASS_IN, cache_flush, (NETIF_TO_HOST(netif))->dns_ttl, (const u8_t *) netif_ip4_addr(netif), sizeof(ip4_addr_t), NULL);
+}
+
+/** Write a 4.3.2.1.in-addr.arpa -> hostname.local PTR RR to outpacket */
+static err_t
+mdns_add_hostv4_ptr_answer(struct mdns_outpacket *reply, u16_t cache_flush, struct netif *netif)
+{
+ struct mdns_domain host, revhost;
+ mdns_build_host_domain(&host, NETIF_TO_HOST(netif));
+ mdns_build_reverse_v4_domain(&revhost, netif_ip4_addr(netif));
+ LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with v4 PTR record\n"));
+ return mdns_add_answer(reply, &revhost, DNS_RRTYPE_PTR, DNS_RRCLASS_IN, cache_flush, (NETIF_TO_HOST(netif))->dns_ttl, NULL, 0, &host);
+}
+#endif
+
+#if LWIP_IPV6
+/** Write an IPv6 address (AAAA) RR to outpacket */
+static err_t
+mdns_add_aaaa_answer(struct mdns_outpacket *reply, u16_t cache_flush, struct netif *netif, int addrindex)
+{
+ struct mdns_domain host;
+ mdns_build_host_domain(&host, NETIF_TO_HOST(netif));
+ LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with AAAA record\n"));
+ return mdns_add_answer(reply, &host, DNS_RRTYPE_AAAA, DNS_RRCLASS_IN, cache_flush, (NETIF_TO_HOST(netif))->dns_ttl, (const u8_t *) netif_ip6_addr(netif, addrindex), sizeof(ip6_addr_t), NULL);
+}
+
+/** Write a x.y.z.ip6.arpa -> hostname.local PTR RR to outpacket */
+static err_t
+mdns_add_hostv6_ptr_answer(struct mdns_outpacket *reply, u16_t cache_flush, struct netif *netif, int addrindex)
+{
+ struct mdns_domain host, revhost;
+ mdns_build_host_domain(&host, NETIF_TO_HOST(netif));
+ mdns_build_reverse_v6_domain(&revhost, netif_ip6_addr(netif, addrindex));
+ LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with v6 PTR record\n"));
+ return mdns_add_answer(reply, &revhost, DNS_RRTYPE_PTR, DNS_RRCLASS_IN, cache_flush, (NETIF_TO_HOST(netif))->dns_ttl, NULL, 0, &host);
+}
+#endif
+
+/** Write an all-services -> servicetype PTR RR to outpacket */
+static err_t
+mdns_add_servicetype_ptr_answer(struct mdns_outpacket *reply, struct mdns_service *service)
+{
+ struct mdns_domain service_type, service_dnssd;
+ mdns_build_service_domain(&service_type, service, 0);
+ mdns_build_dnssd_domain(&service_dnssd);
+ LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with service type PTR record\n"));
+ return mdns_add_answer(reply, &service_dnssd, DNS_RRTYPE_PTR, DNS_RRCLASS_IN, 0, service->dns_ttl, NULL, 0, &service_type);
+}
+
+/** Write a servicetype -> servicename PTR RR to outpacket */
+static err_t
+mdns_add_servicename_ptr_answer(struct mdns_outpacket *reply, struct mdns_service *service)
+{
+ struct mdns_domain service_type, service_instance;
+ mdns_build_service_domain(&service_type, service, 0);
+ mdns_build_service_domain(&service_instance, service, 1);
+ LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with service name PTR record\n"));
+ return mdns_add_answer(reply, &service_type, DNS_RRTYPE_PTR, DNS_RRCLASS_IN, 0, service->dns_ttl, NULL, 0, &service_instance);
+}
+
+/** Write a SRV RR to outpacket */
+static err_t
+mdns_add_srv_answer(struct mdns_outpacket *reply, u16_t cache_flush, struct mdns_host *mdns, struct mdns_service *service)
+{
+ struct mdns_domain service_instance, srvhost;
+ u16_t srvdata[3];
+ mdns_build_service_domain(&service_instance, service, 1);
+ mdns_build_host_domain(&srvhost, mdns);
+ if (reply->legacy_query) {
+ /* RFC 6762 section 18.14:
+ * In legacy unicast responses generated to answer legacy queries,
+ * name compression MUST NOT be performed on SRV records.
+ */
+ srvhost.skip_compression = 1;
+ }
+ srvdata[0] = lwip_htons(SRV_PRIORITY);
+ srvdata[1] = lwip_htons(SRV_WEIGHT);
+ srvdata[2] = lwip_htons(service->port);
+ LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with SRV record\n"));
+ return mdns_add_answer(reply, &service_instance, DNS_RRTYPE_SRV, DNS_RRCLASS_IN, cache_flush, service->dns_ttl,
+ (const u8_t *) &srvdata, sizeof(srvdata), &srvhost);
+}
+
+/** Write a TXT RR to outpacket */
+static err_t
+mdns_add_txt_answer(struct mdns_outpacket *reply, u16_t cache_flush, struct mdns_service *service)
+{
+ struct mdns_domain service_instance;
+ mdns_build_service_domain(&service_instance, service, 1);
+ mdns_prepare_txtdata(service);
+ LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Responding with TXT record\n"));
+ return mdns_add_answer(reply, &service_instance, DNS_RRTYPE_TXT, DNS_RRCLASS_IN, cache_flush, service->dns_ttl,
+ (u8_t *) &service->txtdata.name, service->txtdata.length, NULL);
+}
+
+/**
+ * Setup outpacket as a reply to the incoming packet
+ */
+static void
+mdns_init_outpacket(struct mdns_outpacket *out, struct mdns_packet *in)
+{
+ memset(out, 0, sizeof(struct mdns_outpacket));
+ out->cache_flush = 1;
+ out->netif = in->netif;
+
+ /* Copy source IP/port to use when responding unicast, or to choose
+ * which pcb to use for multicast (IPv4/IPv6)
+ */
+ SMEMCPY(&out->dest_addr, &in->source_addr, sizeof(ip_addr_t));
+ out->dest_port = in->source_port;
+
+ if (in->source_port != MDNS_PORT) {
+ out->unicast_reply = 1;
+ out->cache_flush = 0;
+ if (in->questions == 1) {
+ out->legacy_query = 1;
+ out->tx_id = in->tx_id;
+ }
+ }
+
+ if (in->recv_unicast) {
+ out->unicast_reply = 1;
+ }
+}
+
+/**
+ * Send chosen answers as a reply
+ *
+ * Add all selected answers (first write will allocate pbuf)
+ * Add additional answers based on the selected answers
+ * Send the packet
+ */
+static void
+mdns_send_outpacket(struct mdns_outpacket *outpkt)
+{
+ struct mdns_service *service;
+ err_t res;
+ int i;
+ struct mdns_host* mdns = NETIF_TO_HOST(outpkt->netif);
+
+ /* Write answers to host questions */
+#if LWIP_IPV4
+ if (outpkt->host_replies & REPLY_HOST_A) {
+ res = mdns_add_a_answer(outpkt, outpkt->cache_flush, outpkt->netif);
+ if (res != ERR_OK) {
+ goto cleanup;
+ }
+ outpkt->answers++;
+ }
+ if (outpkt->host_replies & REPLY_HOST_PTR_V4) {
+ res = mdns_add_hostv4_ptr_answer(outpkt, outpkt->cache_flush, outpkt->netif);
+ if (res != ERR_OK) {
+ goto cleanup;
+ }
+ outpkt->answers++;
+ }
+#endif
+#if LWIP_IPV6
+ if (outpkt->host_replies & REPLY_HOST_AAAA) {
+ int addrindex;
+ for (addrindex = 0; addrindex < LWIP_IPV6_NUM_ADDRESSES; ++addrindex) {
+ if (ip6_addr_isvalid(netif_ip6_addr_state(outpkt->netif, addrindex))) {
+ res = mdns_add_aaaa_answer(outpkt, outpkt->cache_flush, outpkt->netif, addrindex);
+ if (res != ERR_OK) {
+ goto cleanup;
+ }
+ outpkt->answers++;
+ }
+ }
+ }
+ if (outpkt->host_replies & REPLY_HOST_PTR_V6) {
+ u8_t rev_addrs = outpkt->host_reverse_v6_replies;
+ int addrindex = 0;
+ while (rev_addrs) {
+ if (rev_addrs & 1) {
+ res = mdns_add_hostv6_ptr_answer(outpkt, outpkt->cache_flush, outpkt->netif, addrindex);
+ if (res != ERR_OK) {
+ goto cleanup;
+ }
+ outpkt->answers++;
+ }
+ addrindex++;
+ rev_addrs >>= 1;
+ }
+ }
+#endif
+
+ /* Write answers to service questions */
+ for (i = 0; i < MDNS_MAX_SERVICES; ++i) {
+ service = mdns->services[i];
+ if (!service) {
+ continue;
+ }
+
+ if (outpkt->serv_replies[i] & REPLY_SERVICE_TYPE_PTR) {
+ res = mdns_add_servicetype_ptr_answer(outpkt, service);
+ if (res != ERR_OK) {
+ goto cleanup;
+ }
+ outpkt->answers++;
+ }
+
+ if (outpkt->serv_replies[i] & REPLY_SERVICE_NAME_PTR) {
+ res = mdns_add_servicename_ptr_answer(outpkt, service);
+ if (res != ERR_OK) {
+ goto cleanup;
+ }
+ outpkt->answers++;
+ }
+
+ if (outpkt->serv_replies[i] & REPLY_SERVICE_SRV) {
+ res = mdns_add_srv_answer(outpkt, outpkt->cache_flush, mdns, service);
+ if (res != ERR_OK) {
+ goto cleanup;
+ }
+ outpkt->answers++;
+ }
+
+ if (outpkt->serv_replies[i] & REPLY_SERVICE_TXT) {
+ res = mdns_add_txt_answer(outpkt, outpkt->cache_flush, service);
+ if (res != ERR_OK) {
+ goto cleanup;
+ }
+ outpkt->answers++;
+ }
+ }
+
+ /* All answers written, add additional RRs */
+ for (i = 0; i < MDNS_MAX_SERVICES; ++i) {
+ service = mdns->services[i];
+ if (!service) {
+ continue;
+ }
+
+ if (outpkt->serv_replies[i] & REPLY_SERVICE_NAME_PTR) {
+ /* Our service instance requested, include SRV & TXT
+ * if they are already not requested. */
+ if (!(outpkt->serv_replies[i] & REPLY_SERVICE_SRV)) {
+ res = mdns_add_srv_answer(outpkt, outpkt->cache_flush, mdns, service);
+ if (res != ERR_OK) {
+ goto cleanup;
+ }
+ outpkt->additional++;
+ }
+
+ if (!(outpkt->serv_replies[i] & REPLY_SERVICE_TXT)) {
+ res = mdns_add_txt_answer(outpkt, outpkt->cache_flush, service);
+ if (res != ERR_OK) {
+ goto cleanup;
+ }
+ outpkt->additional++;
+ }
+ }
+
+ /* If service instance, SRV, record or an IP address is requested,
+ * supply all addresses for the host
+ */
+ if ((outpkt->serv_replies[i] & (REPLY_SERVICE_NAME_PTR | REPLY_SERVICE_SRV)) ||
+ (outpkt->host_replies & (REPLY_HOST_A | REPLY_HOST_AAAA))) {
+#if LWIP_IPV6
+ if (!(outpkt->host_replies & REPLY_HOST_AAAA)) {
+ int addrindex;
+ for (addrindex = 0; addrindex < LWIP_IPV6_NUM_ADDRESSES; ++addrindex) {
+ if (ip6_addr_isvalid(netif_ip6_addr_state(outpkt->netif, addrindex))) {
+ res = mdns_add_aaaa_answer(outpkt, outpkt->cache_flush, outpkt->netif, addrindex);
+ if (res != ERR_OK) {
+ goto cleanup;
+ }
+ outpkt->additional++;
+ }
+ }
+ }
+#endif
+#if LWIP_IPV4
+ if (!(outpkt->host_replies & REPLY_HOST_A)) {
+ res = mdns_add_a_answer(outpkt, outpkt->cache_flush, outpkt->netif);
+ if (res != ERR_OK) {
+ goto cleanup;
+ }
+ outpkt->additional++;
+ }
+#endif
+ }
+ }
+
+ if (outpkt->pbuf) {
+ const ip_addr_t *mcast_destaddr;
+ struct dns_hdr hdr;
+
+ /* Write header */
+ memset(&hdr, 0, sizeof(hdr));
+ hdr.flags1 = DNS_FLAG1_RESPONSE | DNS_FLAG1_AUTHORATIVE;
+ hdr.numanswers = lwip_htons(outpkt->answers);
+ hdr.numextrarr = lwip_htons(outpkt->additional);
+ if (outpkt->legacy_query) {
+ hdr.numquestions = lwip_htons(1);
+ hdr.id = lwip_htons(outpkt->tx_id);
+ }
+ pbuf_take(outpkt->pbuf, &hdr, sizeof(hdr));
+
+ /* Shrink packet */
+ pbuf_realloc(outpkt->pbuf, outpkt->write_offset);
+
+ if (IP_IS_V6_VAL(outpkt->dest_addr)) {
+#if LWIP_IPV6
+ mcast_destaddr = &v6group;
+#endif
+ } else {
+#if LWIP_IPV4
+ mcast_destaddr = &v4group;
+#endif
+ }
+ /* Send created packet */
+ LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Sending packet, len=%d, unicast=%d\n", outpkt->write_offset, outpkt->unicast_reply));
+ if (outpkt->unicast_reply) {
+ udp_sendto_if(mdns_pcb, outpkt->pbuf, &outpkt->dest_addr, outpkt->dest_port, outpkt->netif);
+ } else {
+ udp_sendto_if(mdns_pcb, outpkt->pbuf, mcast_destaddr, MDNS_PORT, outpkt->netif);
+ }
+ }
+
+cleanup:
+ if (outpkt->pbuf) {
+ pbuf_free(outpkt->pbuf);
+ outpkt->pbuf = NULL;
+ }
+}
+
+/**
+ * Send unsolicited answer containing all our known data
+ * @param netif The network interface to send on
+ * @param destination The target address to send to (usually multicast address)
+ */
+static void
+mdns_announce(struct netif *netif, const ip_addr_t *destination)
+{
+ struct mdns_outpacket announce;
+ int i;
+ struct mdns_host* mdns = NETIF_TO_HOST(netif);
+
+ memset(&announce, 0, sizeof(announce));
+ announce.netif = netif;
+ announce.cache_flush = 1;
+#if LWIP_IPV4
+ if (!ip4_addr_isany_val(*netif_ip4_addr(netif)))
+ announce.host_replies = REPLY_HOST_A | REPLY_HOST_PTR_V4;
+#endif
+#if LWIP_IPV6
+ for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; ++i) {
+ if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i))) {
+ announce.host_replies |= REPLY_HOST_AAAA | REPLY_HOST_PTR_V6;
+ announce.host_reverse_v6_replies |= (1 << i);
+ }
+ }
+#endif
+
+ for (i = 0; i < MDNS_MAX_SERVICES; i++) {
+ struct mdns_service *serv = mdns->services[i];
+ if (serv) {
+ announce.serv_replies[i] = REPLY_SERVICE_TYPE_PTR | REPLY_SERVICE_NAME_PTR |
+ REPLY_SERVICE_SRV | REPLY_SERVICE_TXT;
+ }
+ }
+
+ announce.dest_port = MDNS_PORT;
+ SMEMCPY(&announce.dest_addr, destination, sizeof(announce.dest_addr));
+ mdns_send_outpacket(&announce);
+}
+
+/**
+ * Handle question MDNS packet
+ * 1. Parse all questions and set bits what answers to send
+ * 2. Clear pending answers if known answers are supplied
+ * 3. Put chosen answers in new packet and send as reply
+ */
+static void
+mdns_handle_question(struct mdns_packet *pkt)
+{
+ struct mdns_service *service;
+ struct mdns_outpacket reply;
+ int replies = 0;
+ int i;
+ err_t res;
+ struct mdns_host* mdns = NETIF_TO_HOST(pkt->netif);
+
+ mdns_init_outpacket(&reply, pkt);
+
+ while (pkt->questions_left) {
+ struct mdns_question q;
+
+ res = mdns_read_question(pkt, &q);
+ if (res != ERR_OK) {
+ LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Failed to parse question, skipping query packet\n"));
+ return;
+ }
+
+ LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Query for domain "));
+ mdns_domain_debug_print(&q.info.domain);
+ LWIP_DEBUGF(MDNS_DEBUG, (" type %d class %d\n", q.info.type, q.info.klass));
+
+ if (q.unicast) {
+ /* Reply unicast if any question is unicast */
+ reply.unicast_reply = 1;
+ }
+
+ reply.host_replies |= check_host(pkt->netif, &q.info, &reply.host_reverse_v6_replies);
+ replies |= reply.host_replies;
+
+ for (i = 0; i < MDNS_MAX_SERVICES; ++i) {
+ service = mdns->services[i];
+ if (!service) {
+ continue;
+ }
+ reply.serv_replies[i] |= check_service(service, &q.info);
+ replies |= reply.serv_replies[i];
+ }
+
+ if (replies && reply.legacy_query) {
+ /* Add question to reply packet (legacy packet only has 1 question) */
+ res = mdns_add_question(&reply, &q.info.domain, q.info.type, q.info.klass, 0);
+ if (res != ERR_OK) {
+ goto cleanup;
+ }
+ }
+ }
+
+ /* Handle known answers */
+ while (pkt->answers_left) {
+ struct mdns_answer ans;
+ u8_t rev_v6;
+ int match;
+
+ res = mdns_read_answer(pkt, &ans);
+ if (res != ERR_OK) {
+ LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Failed to parse answer, skipping query packet\n"));
+ goto cleanup;
+ }
+
+ LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Known answer for domain "));
+ mdns_domain_debug_print(&ans.info.domain);
+ LWIP_DEBUGF(MDNS_DEBUG, (" type %d class %d\n", ans.info.type, ans.info.klass));
+
+
+ if (ans.info.type == DNS_RRTYPE_ANY || ans.info.klass == DNS_RRCLASS_ANY) {
+ /* Skip known answers for ANY type & class */
+ continue;
+ }
+
+ rev_v6 = 0;
+ match = reply.host_replies & check_host(pkt->netif, &ans.info, &rev_v6);
+ if (match && (ans.ttl > (mdns->dns_ttl / 2))) {
+ /* The RR in the known answer matches an RR we are planning to send,
+ * and the TTL is less than half gone.
+ * If the payload matches we should not send that answer.
+ */
+ if (ans.info.type == DNS_RRTYPE_PTR) {
+ /* Read domain and compare */
+ struct mdns_domain known_ans, my_ans;
+ u16_t len;
+ len = mdns_readname(pkt->pbuf, ans.rd_offset, &known_ans);
+ res = mdns_build_host_domain(&my_ans, mdns);
+ if (len != MDNS_READNAME_ERROR && res == ERR_OK && mdns_domain_eq(&known_ans, &my_ans)) {
+#if LWIP_IPV4
+ if (match & REPLY_HOST_PTR_V4) {
+ LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: v4 PTR\n"));
+ reply.host_replies &= ~REPLY_HOST_PTR_V4;
+ }
+#endif
+#if LWIP_IPV6
+ if (match & REPLY_HOST_PTR_V6) {
+ LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: v6 PTR\n"));
+ reply.host_reverse_v6_replies &= ~rev_v6;
+ if (reply.host_reverse_v6_replies == 0) {
+ reply.host_replies &= ~REPLY_HOST_PTR_V6;
+ }
+ }
+#endif
+ }
+ } else if (match & REPLY_HOST_A) {
+#if LWIP_IPV4
+ if (ans.rd_length == sizeof(ip4_addr_t) &&
+ pbuf_memcmp(pkt->pbuf, ans.rd_offset, netif_ip4_addr(pkt->netif), ans.rd_length) == 0) {
+ LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: A\n"));
+ reply.host_replies &= ~REPLY_HOST_A;
+ }
+#endif
+ } else if (match & REPLY_HOST_AAAA) {
+#if LWIP_IPV6
+ if (ans.rd_length == sizeof(ip6_addr_t) &&
+ /* TODO this clears all AAAA responses if first addr is set as known */
+ pbuf_memcmp(pkt->pbuf, ans.rd_offset, netif_ip6_addr(pkt->netif, 0), ans.rd_length) == 0) {
+ LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: AAAA\n"));
+ reply.host_replies &= ~REPLY_HOST_AAAA;
+ }
+#endif
+ }
+ }
+
+ for (i = 0; i < MDNS_MAX_SERVICES; ++i) {
+ service = mdns->services[i];
+ if (!service) {
+ continue;
+ }
+ match = reply.serv_replies[i] & check_service(service, &ans.info);
+ if (match && (ans.ttl > (service->dns_ttl / 2))) {
+ /* The RR in the known answer matches an RR we are planning to send,
+ * and the TTL is less than half gone.
+ * If the payload matches we should not send that answer.
+ */
+ if (ans.info.type == DNS_RRTYPE_PTR) {
+ /* Read domain and compare */
+ struct mdns_domain known_ans, my_ans;
+ u16_t len;
+ len = mdns_readname(pkt->pbuf, ans.rd_offset, &known_ans);
+ if (len != MDNS_READNAME_ERROR) {
+ if (match & REPLY_SERVICE_TYPE_PTR) {
+ res = mdns_build_service_domain(&my_ans, service, 0);
+ if (res == ERR_OK && mdns_domain_eq(&known_ans, &my_ans)) {
+ LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: service type PTR\n"));
+ reply.serv_replies[i] &= ~REPLY_SERVICE_TYPE_PTR;
+ }
+ }
+ if (match & REPLY_SERVICE_NAME_PTR) {
+ res = mdns_build_service_domain(&my_ans, service, 1);
+ if (res == ERR_OK && mdns_domain_eq(&known_ans, &my_ans)) {
+ LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: service name PTR\n"));
+ reply.serv_replies[i] &= ~REPLY_SERVICE_NAME_PTR;
+ }
+ }
+ }
+ } else if (match & REPLY_SERVICE_SRV) {
+ /* Read and compare to my SRV record */
+ u16_t field16, len, read_pos;
+ struct mdns_domain known_ans, my_ans;
+ read_pos = ans.rd_offset;
+ do {
+ /* Check priority field */
+ len = pbuf_copy_partial(pkt->pbuf, &field16, sizeof(field16), read_pos);
+ if (len != sizeof(field16) || lwip_ntohs(field16) != SRV_PRIORITY) {
+ break;
+ }
+ read_pos += len;
+ /* Check weight field */
+ len = pbuf_copy_partial(pkt->pbuf, &field16, sizeof(field16), read_pos);
+ if (len != sizeof(field16) || lwip_ntohs(field16) != SRV_WEIGHT) {
+ break;
+ }
+ read_pos += len;
+ /* Check port field */
+ len = pbuf_copy_partial(pkt->pbuf, &field16, sizeof(field16), read_pos);
+ if (len != sizeof(field16) || lwip_ntohs(field16) != service->port) {
+ break;
+ }
+ read_pos += len;
+ /* Check host field */
+ len = mdns_readname(pkt->pbuf, read_pos, &known_ans);
+ mdns_build_host_domain(&my_ans, mdns);
+ if (len == MDNS_READNAME_ERROR || !mdns_domain_eq(&known_ans, &my_ans)) {
+ break;
+ }
+ LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: SRV\n"));
+ reply.serv_replies[i] &= ~REPLY_SERVICE_SRV;
+ } while (0);
+ } else if (match & REPLY_SERVICE_TXT) {
+ mdns_prepare_txtdata(service);
+ if (service->txtdata.length == ans.rd_length &&
+ pbuf_memcmp(pkt->pbuf, ans.rd_offset, service->txtdata.name, ans.rd_length) == 0) {
+ LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Skipping known answer: TXT\n"));
+ reply.serv_replies[i] &= ~REPLY_SERVICE_TXT;
+ }
+ }
+ }
+ }
+ }
+
+ mdns_send_outpacket(&reply);
+
+cleanup:
+ if (reply.pbuf) {
+ /* This should only happen if we fail to alloc/write question for legacy query */
+ pbuf_free(reply.pbuf);
+ reply.pbuf = NULL;
+ }
+}
+
+/**
+ * Handle response MDNS packet
+ * Only prints debug for now. Will need more code to do conflict resolution.
+ */
+static void
+mdns_handle_response(struct mdns_packet *pkt)
+{
+ /* Ignore all questions */
+ while (pkt->questions_left) {
+ struct mdns_question q;
+ err_t res;
+
+ res = mdns_read_question(pkt, &q);
+ if (res != ERR_OK) {
+ LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Failed to parse question, skipping response packet\n"));
+ return;
+ }
+ }
+
+ while (pkt->answers_left) {
+ struct mdns_answer ans;
+ err_t res;
+
+ res = mdns_read_answer(pkt, &ans);
+ if (res != ERR_OK) {
+ LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Failed to parse answer, skipping response packet\n"));
+ return;
+ }
+
+ LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Answer for domain "));
+ mdns_domain_debug_print(&ans.info.domain);
+ LWIP_DEBUGF(MDNS_DEBUG, (" type %d class %d\n", ans.info.type, ans.info.klass));
+ }
+}
+
+/**
+ * Receive input function for MDNS packets.
+ * Handles both IPv4 and IPv6 UDP pcbs.
+ */
+static void
+mdns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
+{
+ struct dns_hdr hdr;
+ struct mdns_packet packet;
+ struct netif *recv_netif = ip_current_input_netif();
+ u16_t offset = 0;
+
+ LWIP_UNUSED_ARG(arg);
+ LWIP_UNUSED_ARG(pcb);
+
+ LWIP_DEBUGF(MDNS_DEBUG, ("MDNS: Received IPv%d MDNS packet, len %d\n", IP_IS_V6(addr)? 6 : 4, p->tot_len));
+
+ if (NETIF_TO_HOST(recv_netif) == NULL) {
+ /* From netif not configured for MDNS */
+ goto dealloc;
+ }
+
+ if (pbuf_copy_partial(p, &hdr, SIZEOF_DNS_HDR, offset) < SIZEOF_DNS_HDR) {
+ /* Too small */
+ goto dealloc;
+ }
+ offset += SIZEOF_DNS_HDR;
+
+ if (DNS_HDR_GET_OPCODE(&hdr)) {
+ /* Ignore non-standard queries in multicast packets (RFC 6762, section 18.3) */
+ goto dealloc;
+ }
+
+ memset(&packet, 0, sizeof(packet));
+ SMEMCPY(&packet.source_addr, addr, sizeof(packet.source_addr));
+ packet.source_port = port;
+ packet.netif = recv_netif;
+ packet.pbuf = p;
+ packet.parse_offset = offset;
+ packet.tx_id = lwip_ntohs(hdr.id);
+ packet.questions = packet.questions_left = lwip_ntohs(hdr.numquestions);
+ packet.answers = packet.answers_left = lwip_ntohs(hdr.numanswers) + lwip_ntohs(hdr.numauthrr) + lwip_ntohs(hdr.numextrarr);
+
+#if LWIP_IPV6
+ if (IP_IS_V6(ip_current_dest_addr())) {
+ if (!ip_addr_cmp(ip_current_dest_addr(), &v6group)) {
+ packet.recv_unicast = 1;
+ }
+ }
+#endif
+#if LWIP_IPV4
+ if (!IP_IS_V6(ip_current_dest_addr())) {
+ if (!ip_addr_cmp(ip_current_dest_addr(), &v4group)) {
+ packet.recv_unicast = 1;
+ }
+ }
+#endif
+
+ if (hdr.flags1 & DNS_FLAG1_RESPONSE) {
+ mdns_handle_response(&packet);
+ } else {
+ mdns_handle_question(&packet);
+ }
+
+dealloc:
+ pbuf_free(p);
+}
+
+/**
+ * @ingroup mdns
+ * Initiate MDNS responder. Will open UDP sockets on port 5353
+ */
+void
+mdns_resp_init(void)
+{
+ err_t res;
+
+ mdns_pcb = udp_new_ip_type(IPADDR_TYPE_ANY);
+ LWIP_ASSERT("Failed to allocate pcb", mdns_pcb != NULL);
+#if LWIP_MULTICAST_TX_OPTIONS
+ udp_set_multicast_ttl(mdns_pcb, MDNS_TTL);
+#else
+ mdns_pcb->ttl = MDNS_TTL;
+#endif
+ res = udp_bind(mdns_pcb, IP_ANY_TYPE, MDNS_PORT);
+ LWIP_UNUSED_ARG(res); /* in case of LWIP_NOASSERT */
+ LWIP_ASSERT("Failed to bind pcb", res == ERR_OK);
+ udp_recv(mdns_pcb, mdns_recv, NULL);
+
+ mdns_netif_client_id = netif_alloc_client_data_id();
+}
+
+/**
+ * @ingroup mdns
+ * Announce IP settings have changed on netif.
+ * Call this in your callback registered by netif_set_status_callback().
+ * This function may go away in the future when netif supports registering
+ * multiple callback functions.
+ * @param netif The network interface where settings have changed.
+ */
+void
+mdns_resp_netif_settings_changed(struct netif *netif)
+{
+ LWIP_ERROR("mdns_resp_netif_ip_changed: netif != NULL", (netif != NULL), return);
+
+ if (NETIF_TO_HOST(netif) == NULL) {
+ return;
+ }
+
+ /* Announce on IPv6 and IPv4 */
+#if LWIP_IPV6
+ mdns_announce(netif, IP6_ADDR_ANY);
+#endif
+#if LWIP_IPV4
+ mdns_announce(netif, IP4_ADDR_ANY);
+#endif
+}
+
+/**
+ * @ingroup mdns
+ * Activate MDNS responder for a network interface and send announce packets.
+ * @param netif The network interface to activate.
+ * @param hostname Name to use. Queries for &lt;hostname&gt;.local will be answered
+ * with the IP addresses of the netif. The hostname will be copied, the
+ * given pointer can be on the stack.
+ * @param dns_ttl Validity time in seconds to send out for IP address data in DNS replies
+ * @return ERR_OK if netif was added, an err_t otherwise
+ */
+err_t
+mdns_resp_add_netif(struct netif *netif, const char *hostname, u32_t dns_ttl)
+{
+ err_t res;
+ struct mdns_host* mdns;
+
+ LWIP_ERROR("mdns_resp_add_netif: netif != NULL", (netif != NULL), return ERR_VAL);
+ LWIP_ERROR("mdns_resp_add_netif: Hostname too long", (strlen(hostname) <= MDNS_LABEL_MAXLEN), return ERR_VAL);
+
+ LWIP_ASSERT("mdns_resp_add_netif: Double add", NETIF_TO_HOST(netif) == NULL);
+ mdns = (struct mdns_host *) mem_malloc(sizeof(struct mdns_host));
+ LWIP_ERROR("mdns_resp_add_netif: Alloc failed", (mdns != NULL), return ERR_MEM);
+
+ netif_set_client_data(netif, mdns_netif_client_id, mdns);
+
+ memset(mdns, 0, sizeof(struct mdns_host));
+ MEMCPY(&mdns->name, hostname, LWIP_MIN(MDNS_LABEL_MAXLEN, strlen(hostname)));
+ mdns->dns_ttl = dns_ttl;
+
+ /* Join multicast groups */
+#if LWIP_IPV4
+ res = igmp_joingroup_netif(netif, ip_2_ip4(&v4group));
+ if (res != ERR_OK) {
+ goto cleanup;
+ }
+#endif
+#if LWIP_IPV6
+ res = mld6_joingroup_netif(netif, ip_2_ip6(&v6group));
+ if (res != ERR_OK) {
+ goto cleanup;
+ }
+#endif
+
+ mdns_resp_netif_settings_changed(netif);
+ return ERR_OK;
+
+cleanup:
+ mem_free(mdns);
+ netif_set_client_data(netif, mdns_netif_client_id, NULL);
+ return res;
+}
+
+/**
+ * @ingroup mdns
+ * Stop responding to MDNS queries on this interface, leave multicast groups,
+ * and free the helper structure and any of its services.
+ * @param netif The network interface to remove.
+ * @return ERR_OK if netif was removed, an err_t otherwise
+ */
+err_t
+mdns_resp_remove_netif(struct netif *netif)
+{
+ int i;
+ struct mdns_host* mdns;
+
+ LWIP_ASSERT("mdns_resp_remove_netif: Null pointer", netif);
+ mdns = NETIF_TO_HOST(netif);
+ LWIP_ERROR("mdns_resp_remove_netif: Not an active netif", (mdns != NULL), return ERR_VAL);
+
+ for (i = 0; i < MDNS_MAX_SERVICES; i++) {
+ struct mdns_service *service = mdns->services[i];
+ if (service) {
+ mem_free(service);
+ }
+ }
+
+ /* Leave multicast groups */
+#if LWIP_IPV4
+ igmp_leavegroup_netif(netif, ip_2_ip4(&v4group));
+#endif
+#if LWIP_IPV6
+ mld6_leavegroup_netif(netif, ip_2_ip6(&v6group));
+#endif
+
+ mem_free(mdns);
+ netif_set_client_data(netif, mdns_netif_client_id, NULL);
+ return ERR_OK;
+}
+
+/**
+ * @ingroup mdns
+ * Add a service to the selected network interface.
+ * @param netif The network interface to publish this service on
+ * @param name The name of the service
+ * @param service The service type, like "_http"
+ * @param proto The service protocol, DNSSD_PROTO_TCP for TCP ("_tcp") and DNSSD_PROTO_UDP
+ * for others ("_udp")
+ * @param port The port the service listens to
+ * @param dns_ttl Validity time in seconds to send out for service data in DNS replies
+ * @param txt_fn Callback to get TXT data. Will be called each time a TXT reply is created to
+ * allow dynamic replies.
+ * @param txt_data Userdata pointer for txt_fn
+ * @return ERR_OK if the service was added to the netif, an err_t otherwise
+ */
+err_t
+mdns_resp_add_service(struct netif *netif, const char *name, const char *service, enum mdns_sd_proto proto, u16_t port, u32_t dns_ttl, service_get_txt_fn_t txt_fn, void *txt_data)
+{
+ int i;
+ int slot = -1;
+ struct mdns_service *srv;
+ struct mdns_host* mdns;
+
+ LWIP_ASSERT("mdns_resp_add_service: netif != NULL", netif);
+ mdns = NETIF_TO_HOST(netif);
+ LWIP_ERROR("mdns_resp_add_service: Not an mdns netif", (mdns != NULL), return ERR_VAL);
+
+ LWIP_ERROR("mdns_resp_add_service: Name too long", (strlen(name) <= MDNS_LABEL_MAXLEN), return ERR_VAL);
+ LWIP_ERROR("mdns_resp_add_service: Service too long", (strlen(service) <= MDNS_LABEL_MAXLEN), return ERR_VAL);
+ LWIP_ERROR("mdns_resp_add_service: Bad proto (need TCP or UDP)", (proto == DNSSD_PROTO_TCP || proto == DNSSD_PROTO_UDP), return ERR_VAL);
+
+ for (i = 0; i < MDNS_MAX_SERVICES; i++) {
+ if (mdns->services[i] == NULL) {
+ slot = i;
+ break;
+ }
+ }
+ LWIP_ERROR("mdns_resp_add_service: Service list full (increase MDNS_MAX_SERVICES)", (slot >= 0), return ERR_MEM);
+
+ srv = (struct mdns_service*)mem_malloc(sizeof(struct mdns_service));
+ LWIP_ERROR("mdns_resp_add_service: Alloc failed", (srv != NULL), return ERR_MEM);
+
+ memset(srv, 0, sizeof(struct mdns_service));
+
+ MEMCPY(&srv->name, name, LWIP_MIN(MDNS_LABEL_MAXLEN, strlen(name)));
+ MEMCPY(&srv->service, service, LWIP_MIN(MDNS_LABEL_MAXLEN, strlen(service)));
+ srv->txt_fn = txt_fn;
+ srv->txt_userdata = txt_data;
+ srv->proto = (u16_t)proto;
+ srv->port = port;
+ srv->dns_ttl = dns_ttl;
+
+ mdns->services[slot] = srv;
+
+ /* Announce on IPv6 and IPv4 */
+#if LWIP_IPV6
+ mdns_announce(netif, IP6_ADDR_ANY);
+#endif
+#if LWIP_IPV4
+ mdns_announce(netif, IP4_ADDR_ANY);
+#endif
+
+ return ERR_OK;
+}
+
+/**
+ * @ingroup mdns
+ * Call this function from inside the service_get_txt_fn_t callback to add text data.
+ * Buffer for TXT data is 256 bytes, and each field is prefixed with a length byte.
+ * @param service The service provided to the get_txt callback
+ * @param txt String to add to the TXT field.
+ * @param txt_len Length of string
+ * @return ERR_OK if the string was added to the reply, an err_t otherwise
+ */
+err_t
+mdns_resp_add_service_txtitem(struct mdns_service *service, const char *txt, u8_t txt_len)
+{
+ LWIP_ASSERT("mdns_resp_add_service_txtitem: service != NULL", service);
+
+ /* Use a mdns_domain struct to store txt chunks since it is the same encoding */
+ return mdns_domain_add_label(&service->txtdata, txt, txt_len);
+}
+
+#endif /* LWIP_MDNS_RESPONDER */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/mqtt/mqtt.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/mqtt/mqtt.c
new file mode 100644
index 0000000..a0e77b9
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/mqtt/mqtt.c
@@ -0,0 +1,1341 @@
+/**
+ * @file
+ * MQTT client
+ *
+ * @defgroup mqtt MQTT client
+ * @ingroup apps
+ * @verbinclude mqtt_client.txt
+ */
+
+/*
+ * Copyright (c) 2016 Erik Andersson <erian747@gmail.com>
+ * 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: Erik Andersson <erian747@gmail.com>
+ *
+ *
+ * @todo:
+ * - Handle large outgoing payloads for PUBLISH messages
+ * - Fix restriction of a single topic in each (UN)SUBSCRIBE message (protocol has support for multiple topics)
+ * - Add support for legacy MQTT protocol version
+ *
+ * Please coordinate changes and requests with Erik Andersson
+ * Erik Andersson <erian747@gmail.com>
+ *
+ */
+#include "lwip/apps/mqtt.h"
+#include "lwip/timeouts.h"
+#include "lwip/ip_addr.h"
+#include "lwip/mem.h"
+#include "lwip/err.h"
+#include "lwip/pbuf.h"
+#include "lwip/tcp.h"
+#include <string.h>
+
+#if LWIP_TCP && LWIP_CALLBACK_API
+
+/**
+ * MQTT_DEBUG: Default is off.
+ */
+#if !defined MQTT_DEBUG || defined __DOXYGEN__
+#define MQTT_DEBUG LWIP_DBG_OFF
+#endif
+
+#define MQTT_DEBUG_TRACE (MQTT_DEBUG | LWIP_DBG_TRACE)
+#define MQTT_DEBUG_STATE (MQTT_DEBUG | LWIP_DBG_STATE)
+#define MQTT_DEBUG_WARN (MQTT_DEBUG | LWIP_DBG_LEVEL_WARNING)
+#define MQTT_DEBUG_WARN_STATE (MQTT_DEBUG | LWIP_DBG_LEVEL_WARNING | LWIP_DBG_STATE)
+#define MQTT_DEBUG_SERIOUS (MQTT_DEBUG | LWIP_DBG_LEVEL_SERIOUS)
+
+static void mqtt_cyclic_timer(void *arg);
+
+/**
+ * MQTT client connection states
+ */
+enum {
+ TCP_DISCONNECTED,
+ TCP_CONNECTING,
+ MQTT_CONNECTING,
+ MQTT_CONNECTED
+};
+
+/**
+ * MQTT control message types
+ */
+enum mqtt_message_type {
+ MQTT_MSG_TYPE_CONNECT = 1,
+ MQTT_MSG_TYPE_CONNACK = 2,
+ MQTT_MSG_TYPE_PUBLISH = 3,
+ MQTT_MSG_TYPE_PUBACK = 4,
+ MQTT_MSG_TYPE_PUBREC = 5,
+ MQTT_MSG_TYPE_PUBREL = 6,
+ MQTT_MSG_TYPE_PUBCOMP = 7,
+ MQTT_MSG_TYPE_SUBSCRIBE = 8,
+ MQTT_MSG_TYPE_SUBACK = 9,
+ MQTT_MSG_TYPE_UNSUBSCRIBE = 10,
+ MQTT_MSG_TYPE_UNSUBACK = 11,
+ MQTT_MSG_TYPE_PINGREQ = 12,
+ MQTT_MSG_TYPE_PINGRESP = 13,
+ MQTT_MSG_TYPE_DISCONNECT = 14
+};
+
+/** Helpers to extract control packet type and qos from first byte in fixed header */
+#define MQTT_CTL_PACKET_TYPE(fixed_hdr_byte0) ((fixed_hdr_byte0 & 0xf0) >> 4)
+#define MQTT_CTL_PACKET_QOS(fixed_hdr_byte0) ((fixed_hdr_byte0 & 0x6) >> 1)
+
+/**
+ * MQTT connect flags, only used in CONNECT message
+ */
+enum mqtt_connect_flag {
+ MQTT_CONNECT_FLAG_USERNAME = 1 << 7,
+ MQTT_CONNECT_FLAG_PASSWORD = 1 << 6,
+ MQTT_CONNECT_FLAG_WILL_RETAIN = 1 << 5,
+ MQTT_CONNECT_FLAG_WILL = 1 << 2,
+ MQTT_CONNECT_FLAG_CLEAN_SESSION = 1 << 1
+};
+
+
+#if defined(LWIP_DEBUG)
+static const char * const mqtt_message_type_str[15] =
+{
+ "UNDEFINED",
+ "CONNECT",
+ "CONNACK",
+ "PUBLISH",
+ "PUBACK",
+ "PUBREC",
+ "PUBREL",
+ "PUBCOMP",
+ "SUBSCRIBE",
+ "SUBACK",
+ "UNSUBSCRIBE",
+ "UNSUBACK",
+ "PINGREQ",
+ "PINGRESP",
+ "DISCONNECT"
+};
+
+/**
+ * Message type value to string
+ * @param msg_type see enum mqtt_message_type
+ *
+ * @return Control message type text string
+ */
+static const char *
+mqtt_msg_type_to_str(u8_t msg_type)
+{
+ if (msg_type >= LWIP_ARRAYSIZE(mqtt_message_type_str)) {
+ msg_type = 0;
+ }
+ return mqtt_message_type_str[msg_type];
+}
+
+#endif
+
+
+/**
+ * Generate MQTT packet identifier
+ * @param client MQTT client
+ * @return New packet identifier, range 1 to 65535
+ */
+static u16_t
+msg_generate_packet_id(mqtt_client_t *client)
+{
+ client->pkt_id_seq++;
+ if (client->pkt_id_seq == 0) {
+ client->pkt_id_seq++;
+ }
+ return client->pkt_id_seq;
+}
+
+/*--------------------------------------------------------------------------------------------------------------------- */
+/* Output ring buffer */
+
+
+#define MQTT_RINGBUF_IDX_MASK ((MQTT_OUTPUT_RINGBUF_SIZE) - 1)
+
+/** Add single item to ring buffer */
+#define mqtt_ringbuf_put(rb, item) ((rb)->buf)[(rb)->put++ & MQTT_RINGBUF_IDX_MASK] = (item)
+
+/** Return number of bytes in ring buffer */
+#define mqtt_ringbuf_len(rb) ((u16_t)((rb)->put - (rb)->get))
+
+/** Return number of bytes free in ring buffer */
+#define mqtt_ringbuf_free(rb) (MQTT_OUTPUT_RINGBUF_SIZE - mqtt_ringbuf_len(rb))
+
+/** Return number of bytes possible to read without wrapping around */
+#define mqtt_ringbuf_linear_read_length(rb) LWIP_MIN(mqtt_ringbuf_len(rb), (MQTT_OUTPUT_RINGBUF_SIZE - ((rb)->get & MQTT_RINGBUF_IDX_MASK)))
+
+/** Return pointer to ring buffer get position */
+#define mqtt_ringbuf_get_ptr(rb) (&(rb)->buf[(rb)->get & MQTT_RINGBUF_IDX_MASK])
+
+#define mqtt_ringbuf_advance_get_idx(rb, len) ((rb)->get += (len))
+
+
+/**
+ * Try send as many bytes as possible from output ring buffer
+ * @param rb Output ring buffer
+ * @param tpcb TCP connection handle
+ */
+static void
+mqtt_output_send(struct mqtt_ringbuf_t *rb, struct tcp_pcb *tpcb)
+{
+ err_t err;
+ u8_t wrap = 0;
+ u16_t ringbuf_lin_len = mqtt_ringbuf_linear_read_length(rb);
+ u16_t send_len = tcp_sndbuf(tpcb);
+ LWIP_ASSERT("mqtt_output_send: tpcb != NULL", tpcb != NULL);
+
+ if (send_len == 0 || ringbuf_lin_len == 0) {
+ return;
+ }
+
+ LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_output_send: tcp_sndbuf: %d bytes, ringbuf_linear_available: %d, get %d, put %d\n",
+ send_len, ringbuf_lin_len, ((rb)->get & MQTT_RINGBUF_IDX_MASK), ((rb)->put & MQTT_RINGBUF_IDX_MASK)));
+
+ if (send_len > ringbuf_lin_len) {
+ /* Space in TCP output buffer is larger than available in ring buffer linear portion */
+ send_len = ringbuf_lin_len;
+ /* Wrap around if more data in ring buffer after linear portion */
+ wrap = (mqtt_ringbuf_len(rb) > ringbuf_lin_len);
+ }
+ err = tcp_write(tpcb, mqtt_ringbuf_get_ptr(rb), send_len, TCP_WRITE_FLAG_COPY | (wrap ? TCP_WRITE_FLAG_MORE : 0));
+ if ((err == ERR_OK) && wrap) {
+ mqtt_ringbuf_advance_get_idx(rb, send_len);
+ /* Use the lesser one of ring buffer linear length and TCP send buffer size */
+ send_len = LWIP_MIN(tcp_sndbuf(tpcb), mqtt_ringbuf_linear_read_length(rb));
+ err = tcp_write(tpcb, mqtt_ringbuf_get_ptr(rb), send_len, TCP_WRITE_FLAG_COPY);
+ }
+
+ if (err == ERR_OK) {
+ mqtt_ringbuf_advance_get_idx(rb, send_len);
+ /* Flush */
+ tcp_output(tpcb);
+ } else {
+ LWIP_DEBUGF(MQTT_DEBUG_WARN, ("mqtt_output_send: Send failed with err %d (\"%s\")\n", err, lwip_strerr(err)));
+ }
+}
+
+
+
+/*--------------------------------------------------------------------------------------------------------------------- */
+/* Request queue */
+
+/**
+ * Create request item
+ * @param r_objs Pointer to request objects
+ * @param pkt_id Packet identifier of request
+ * @param cb Packet callback to call when requests lifetime ends
+ * @param arg Parameter following callback
+ * @return Request or NULL if failed to create
+ */
+static struct mqtt_request_t *
+mqtt_create_request(struct mqtt_request_t *r_objs, u16_t pkt_id, mqtt_request_cb_t cb, void *arg)
+{
+ struct mqtt_request_t *r = NULL;
+ u8_t n;
+ LWIP_ASSERT("mqtt_create_request: r_objs != NULL", r_objs != NULL);
+ for (n = 0; n < MQTT_REQ_MAX_IN_FLIGHT; n++) {
+ /* Item point to itself if not in use */
+ if (r_objs[n].next == &r_objs[n]) {
+ r = &r_objs[n];
+ r->next = NULL;
+ r->cb = cb;
+ r->arg = arg;
+ r->pkt_id = pkt_id;
+ break;
+ }
+ }
+ return r;
+}
+
+
+/**
+ * Append request to pending request queue
+ * @param tail Pointer to request queue tail pointer
+ * @param r Request to append
+ */
+static void
+mqtt_append_request(struct mqtt_request_t **tail, struct mqtt_request_t *r)
+{
+ struct mqtt_request_t *head = NULL;
+ s16_t time_before = 0;
+ struct mqtt_request_t *iter;
+
+ LWIP_ASSERT("mqtt_append_request: tail != NULL", tail != NULL);
+
+ /* Iterate trough queue to find head, and count total timeout time */
+ for (iter = *tail; iter != NULL; iter = iter->next) {
+ time_before += iter->timeout_diff;
+ head = iter;
+ }
+
+ LWIP_ASSERT("mqtt_append_request: time_before <= MQTT_REQ_TIMEOUT", time_before <= MQTT_REQ_TIMEOUT);
+ r->timeout_diff = MQTT_REQ_TIMEOUT - time_before;
+ if (head == NULL) {
+ *tail = r;
+ } else {
+ head->next = r;
+ }
+}
+
+
+/**
+ * Delete request item
+ * @param r Request item to delete
+ */
+static void
+mqtt_delete_request(struct mqtt_request_t *r)
+{
+ if (r != NULL) {
+ r->next = r;
+ }
+}
+
+/**
+ * Remove a request item with a specific packet identifier from request queue
+ * @param tail Pointer to request queue tail pointer
+ * @param pkt_id Packet identifier of request to take
+ * @return Request item if found, NULL if not
+ */
+static struct mqtt_request_t *
+mqtt_take_request(struct mqtt_request_t **tail, u16_t pkt_id)
+{
+ struct mqtt_request_t *iter = NULL, *prev = NULL;
+ LWIP_ASSERT("mqtt_take_request: tail != NULL", tail != NULL);
+ /* Search all request for pkt_id */
+ for (iter = *tail; iter != NULL; iter = iter->next) {
+ if (iter->pkt_id == pkt_id) {
+ break;
+ }
+ prev = iter;
+ }
+
+ /* If request was found */
+ if (iter != NULL) {
+ /* unchain */
+ if (prev == NULL) {
+ *tail= iter->next;
+ } else {
+ prev->next = iter->next;
+ }
+ /* If exists, add remaining timeout time for the request to next */
+ if (iter->next != NULL) {
+ iter->next->timeout_diff += iter->timeout_diff;
+ }
+ iter->next = NULL;
+ }
+ return iter;
+}
+
+/**
+ * Handle requests timeout
+ * @param tail Pointer to request queue tail pointer
+ * @param t Time since last call in seconds
+ */
+static void
+mqtt_request_time_elapsed(struct mqtt_request_t **tail, u8_t t)
+{
+ struct mqtt_request_t *r = *tail;
+ LWIP_ASSERT("mqtt_request_time_elapsed: tail != NULL", tail != NULL);
+ while (t > 0 && r != NULL) {
+ if (t >= r->timeout_diff) {
+ t -= (u8_t)r->timeout_diff;
+ /* Unchain */
+ *tail = r->next;
+ /* Notify upper layer about timeout */
+ if (r->cb != NULL) {
+ r->cb(r->arg, ERR_TIMEOUT);
+ }
+ mqtt_delete_request(r);
+ /* Tail might be be modified in callback, so re-read it in every iteration */
+ r = *(struct mqtt_request_t * const volatile *)tail;
+ } else {
+ r->timeout_diff -= t;
+ t = 0;
+ }
+ }
+}
+
+/**
+ * Free all request items
+ * @param tail Pointer to request queue tail pointer
+ */
+static void
+mqtt_clear_requests(struct mqtt_request_t **tail)
+{
+ struct mqtt_request_t *iter, *next;
+ LWIP_ASSERT("mqtt_clear_requests: tail != NULL", tail != NULL);
+ for (iter = *tail; iter != NULL; iter = next) {
+ next = iter->next;
+ mqtt_delete_request(iter);
+ }
+ *tail = NULL;
+}
+/**
+ * Initialize all request items
+ * @param r_objs Pointer to request objects
+ */
+static void
+mqtt_init_requests(struct mqtt_request_t *r_objs)
+{
+ u8_t n;
+ LWIP_ASSERT("mqtt_init_requests: r_objs != NULL", r_objs != NULL);
+ for (n = 0; n < MQTT_REQ_MAX_IN_FLIGHT; n++) {
+ /* Item pointing to itself indicates unused */
+ r_objs[n].next = &r_objs[n];
+ }
+}
+
+/*--------------------------------------------------------------------------------------------------------------------- */
+/* Output message build helpers */
+
+
+static void
+mqtt_output_append_u8(struct mqtt_ringbuf_t *rb, u8_t value)
+{
+ mqtt_ringbuf_put(rb, value);
+}
+
+static
+void mqtt_output_append_u16(struct mqtt_ringbuf_t *rb, u16_t value)
+{
+ mqtt_ringbuf_put(rb, value >> 8);
+ mqtt_ringbuf_put(rb, value & 0xff);
+}
+
+static void
+mqtt_output_append_buf(struct mqtt_ringbuf_t *rb, const void *data, u16_t length)
+{
+ u16_t n;
+ for (n = 0; n < length; n++) {
+ mqtt_ringbuf_put(rb, ((const u8_t *)data)[n]);
+ }
+}
+
+static void
+mqtt_output_append_string(struct mqtt_ringbuf_t *rb, const char *str, u16_t length)
+{
+ u16_t n;
+ mqtt_ringbuf_put(rb, length >> 8);
+ mqtt_ringbuf_put(rb, length & 0xff);
+ for (n = 0; n < length; n++) {
+ mqtt_ringbuf_put(rb, str[n]);
+ }
+}
+
+/**
+ * Append fixed header
+ * @param rb Output ring buffer
+ * @param msg_type see enum mqtt_message_type
+ * @param dup MQTT DUP flag
+ * @param qos MQTT QoS field
+ * @param retain MQTT retain flag
+ * @param r_length Remaining length after fixed header
+ */
+
+static void
+mqtt_output_append_fixed_header(struct mqtt_ringbuf_t *rb, u8_t msg_type, u8_t dup,
+ u8_t qos, u8_t retain, u16_t r_length)
+{
+ /* Start with control byte */
+ mqtt_output_append_u8(rb, (((msg_type & 0x0f) << 4) | ((dup & 1) << 3) | ((qos & 3) << 1) | (retain & 1)));
+ /* Encode remaining length field */
+ do {
+ mqtt_output_append_u8(rb, (r_length & 0x7f) | (r_length >= 128 ? 0x80 : 0));
+ r_length >>= 7;
+ } while (r_length > 0);
+}
+
+
+/**
+ * Check output buffer space
+ * @param rb Output ring buffer
+ * @param r_length Remaining length after fixed header
+ * @return 1 if message will fit, 0 if not enough buffer space
+ */
+static u8_t
+mqtt_output_check_space(struct mqtt_ringbuf_t *rb, u16_t r_length)
+{
+ /* Start with length of type byte + remaining length */
+ u16_t total_len = 1 + r_length;
+
+ LWIP_ASSERT("mqtt_output_check_space: rb != NULL", rb != NULL);
+
+ /* Calculate number of required bytes to contain the remaining bytes field and add to total*/
+ do {
+ total_len++;
+ r_length >>= 7;
+ } while (r_length > 0);
+
+ return (total_len <= mqtt_ringbuf_free(rb));
+}
+
+
+/**
+ * Close connection to server
+ * @param client MQTT client
+ * @param reason Reason for disconnection
+ */
+static void
+mqtt_close(mqtt_client_t *client, mqtt_connection_status_t reason)
+{
+ LWIP_ASSERT("mqtt_close: client != NULL", client != NULL);
+
+ /* Bring down TCP connection if not already done */
+ if (client->conn != NULL) {
+ err_t res;
+ tcp_recv(client->conn, NULL);
+ tcp_err(client->conn, NULL);
+ tcp_sent(client->conn, NULL);
+ res = tcp_close(client->conn);
+ if (res != ERR_OK) {
+ tcp_abort(client->conn);
+ LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_close: Close err=%s\n", lwip_strerr(res)));
+ }
+ client->conn = NULL;
+ }
+
+ /* Remove all pending requests */
+ mqtt_clear_requests(&client->pend_req_queue);
+ /* Stop cyclic timer */
+ sys_untimeout(mqtt_cyclic_timer, client);
+
+ /* Notify upper layer of disconnection if changed state */
+ if (client->conn_state != TCP_DISCONNECTED) {
+
+ client->conn_state = TCP_DISCONNECTED;
+ if (client->connect_cb != NULL) {
+ client->connect_cb(client, client->connect_arg, reason);
+ }
+ }
+}
+
+
+/**
+ * Interval timer, called every MQTT_CYCLIC_TIMER_INTERVAL seconds in MQTT_CONNECTING and MQTT_CONNECTED states
+ * @param arg MQTT client
+ */
+static void
+mqtt_cyclic_timer(void *arg)
+{
+ u8_t restart_timer = 1;
+ mqtt_client_t *client = (mqtt_client_t *)arg;
+ LWIP_ASSERT("mqtt_cyclic_timer: client != NULL", client != NULL);
+
+ if (client->conn_state == MQTT_CONNECTING) {
+ client->cyclic_tick++;
+ if ((client->cyclic_tick * MQTT_CYCLIC_TIMER_INTERVAL) >= MQTT_CONNECT_TIMOUT) {
+ LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_cyclic_timer: CONNECT attempt to server timed out\n"));
+ /* Disconnect TCP */
+ mqtt_close(client, MQTT_CONNECT_TIMEOUT);
+ restart_timer = 0;
+ }
+ } else if (client->conn_state == MQTT_CONNECTED) {
+ /* Handle timeout for pending requests */
+ mqtt_request_time_elapsed(&client->pend_req_queue, MQTT_CYCLIC_TIMER_INTERVAL);
+
+ /* keep_alive > 0 means keep alive functionality shall be used */
+ if (client->keep_alive > 0) {
+
+ client->server_watchdog++;
+ /* If reception from server has been idle for 1.5*keep_alive time, server is considered unresponsive */
+ if ((client->server_watchdog * MQTT_CYCLIC_TIMER_INTERVAL) > (client->keep_alive + client->keep_alive/2)) {
+ LWIP_DEBUGF(MQTT_DEBUG_WARN,("mqtt_cyclic_timer: Server incoming keep-alive timeout\n"));
+ mqtt_close(client, MQTT_CONNECT_TIMEOUT);
+ restart_timer = 0;
+ }
+
+ /* If time for a keep alive message to be sent, transmission has been idle for keep_alive time */
+ if ((client->cyclic_tick * MQTT_CYCLIC_TIMER_INTERVAL) >= client->keep_alive) {
+ LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_cyclic_timer: Sending keep-alive message to server\n"));
+ if (mqtt_output_check_space(&client->output, 0) != 0) {
+ mqtt_output_append_fixed_header(&client->output, MQTT_MSG_TYPE_PINGREQ, 0, 0, 0, 0);
+ client->cyclic_tick = 0;
+ }
+ } else {
+ client->cyclic_tick++;
+ }
+ }
+ } else {
+ LWIP_DEBUGF(MQTT_DEBUG_WARN,("mqtt_cyclic_timer: Timer should not be running in state %d\n", client->conn_state));
+ restart_timer = 0;
+ }
+ if (restart_timer) {
+ sys_timeout(MQTT_CYCLIC_TIMER_INTERVAL*1000, mqtt_cyclic_timer, arg);
+ }
+}
+
+
+/**
+ * Send PUBACK, PUBREC or PUBREL response message
+ * @param client MQTT client
+ * @param msg PUBACK, PUBREC or PUBREL
+ * @param pkt_id Packet identifier
+ * @param qos QoS value
+ * @return ERR_OK if successful, ERR_MEM if out of memory
+ */
+static err_t
+pub_ack_rec_rel_response(mqtt_client_t *client, u8_t msg, u16_t pkt_id, u8_t qos)
+{
+ err_t err = ERR_OK;
+ if (mqtt_output_check_space(&client->output, 2)) {
+ mqtt_output_append_fixed_header(&client->output, msg, 0, qos, 0, 2);
+ mqtt_output_append_u16(&client->output, pkt_id);
+ mqtt_output_send(&client->output, client->conn);
+ } else {
+ LWIP_DEBUGF(MQTT_DEBUG_TRACE,("pub_ack_rec_rel_response: OOM creating response: %s with pkt_id: %d\n",
+ mqtt_msg_type_to_str(msg), pkt_id));
+ err = ERR_MEM;
+ }
+ return err;
+}
+
+/**
+ * Subscribe response from server
+ * @param r Matching request
+ * @param result Result code from server
+ */
+static void
+mqtt_incomming_suback(struct mqtt_request_t *r, u8_t result)
+{
+ if (r->cb != NULL) {
+ r->cb(r->arg, result < 3 ? ERR_OK : ERR_ABRT);
+ }
+}
+
+
+/**
+ * Complete MQTT message received or buffer full
+ * @param client MQTT client
+ * @param fixed_hdr_idx header index
+ * @param length length received part
+ * @param remaining_length Remaining length of complete message
+ */
+static mqtt_connection_status_t
+ mqtt_message_received(mqtt_client_t *client, u8_t fixed_hdr_idx, u16_t length, u32_t remaining_length)
+{
+ mqtt_connection_status_t res = MQTT_CONNECT_ACCEPTED;
+
+ u8_t *var_hdr_payload = client->rx_buffer + fixed_hdr_idx;
+
+ /* Control packet type */
+ u8_t pkt_type = MQTT_CTL_PACKET_TYPE(client->rx_buffer[0]);
+ u16_t pkt_id = 0;
+
+ if (pkt_type == MQTT_MSG_TYPE_CONNACK) {
+ if (client->conn_state == MQTT_CONNECTING) {
+ /* Get result code from CONNACK */
+ res = (mqtt_connection_status_t)var_hdr_payload[1];
+ LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_message_received: Connect response code %d\n", res));
+ if (res == MQTT_CONNECT_ACCEPTED) {
+ /* Reset cyclic_tick when changing to connected state */
+ client->cyclic_tick = 0;
+ client->conn_state = MQTT_CONNECTED;
+ /* Notify upper layer */
+ if (client->connect_cb != 0) {
+ client->connect_cb(client, client->connect_arg, res);
+ }
+ }
+ } else {
+ LWIP_DEBUGF(MQTT_DEBUG_WARN,("mqtt_message_received: Received CONNACK in connected state\n"));
+ }
+ } else if (pkt_type == MQTT_MSG_TYPE_PINGRESP) {
+ LWIP_DEBUGF(MQTT_DEBUG_TRACE,( "mqtt_message_received: Received PINGRESP from server\n"));
+
+ } else if (pkt_type == MQTT_MSG_TYPE_PUBLISH) {
+ u16_t payload_offset = 0;
+ u16_t payload_length = length;
+ u8_t qos = MQTT_CTL_PACKET_QOS(client->rx_buffer[0]);
+
+ if (client->msg_idx <= MQTT_VAR_HEADER_BUFFER_LEN) {
+ /* Should have topic and pkt id*/
+ uint8_t *topic;
+ uint16_t after_topic;
+ u8_t bkp;
+ u16_t topic_len = var_hdr_payload[0];
+ topic_len = (topic_len << 8) + (u16_t)(var_hdr_payload[1]);
+
+ topic = var_hdr_payload + 2;
+ after_topic = 2 + topic_len;
+ /* Check length, add one byte even for QoS 0 so that zero termination will fit */
+ if ((after_topic + (qos? 2 : 1)) > length) {
+ LWIP_DEBUGF(MQTT_DEBUG_WARN,("mqtt_message_received: Receive buffer can not fit topic + pkt_id\n"));
+ goto out_disconnect;
+ }
+
+ /* id for QoS 1 and 2 */
+ if (qos > 0) {
+ client->inpub_pkt_id = ((u16_t)var_hdr_payload[after_topic] << 8) + (u16_t)var_hdr_payload[after_topic + 1];
+ after_topic += 2;
+ } else {
+ client->inpub_pkt_id = 0;
+ }
+ /* Take backup of byte after topic */
+ bkp = topic[topic_len];
+ /* Zero terminate string */
+ topic[topic_len] = 0;
+ /* Payload data remaining in receive buffer */
+ payload_length = length - after_topic;
+ payload_offset = after_topic;
+
+ LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_incomming_publish: Received message with QoS %d at topic: %s, payload length %d\n",
+ qos, topic, remaining_length + payload_length));
+ if (client->pub_cb != NULL) {
+ client->pub_cb(client->inpub_arg, (const char *)topic, remaining_length + payload_length);
+ }
+ /* Restore byte after topic */
+ topic[topic_len] = bkp;
+ }
+ if (payload_length > 0 || remaining_length == 0) {
+ client->data_cb(client->inpub_arg, var_hdr_payload + payload_offset, payload_length, remaining_length == 0 ? MQTT_DATA_FLAG_LAST : 0);
+ /* Reply if QoS > 0 */
+ if (remaining_length == 0 && qos > 0) {
+ /* Send PUBACK for QoS 1 or PUBREC for QoS 2 */
+ u8_t resp_msg = (qos == 1) ? MQTT_MSG_TYPE_PUBACK : MQTT_MSG_TYPE_PUBREC;
+ LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_incomming_publish: Sending publish response: %s with pkt_id: %d\n",
+ mqtt_msg_type_to_str(resp_msg), client->inpub_pkt_id));
+ pub_ack_rec_rel_response(client, resp_msg, client->inpub_pkt_id, 0);
+ }
+ }
+ } else {
+ /* Get packet identifier */
+ pkt_id = (u16_t)var_hdr_payload[0] << 8;
+ pkt_id |= (u16_t)var_hdr_payload[1];
+ if (pkt_id == 0) {
+ LWIP_DEBUGF(MQTT_DEBUG_WARN,("mqtt_message_received: Got message with illegal packet identifier: 0\n"));
+ goto out_disconnect;
+ }
+ if (pkt_type == MQTT_MSG_TYPE_PUBREC) {
+ LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_message_received: PUBREC, sending PUBREL with pkt_id: %d\n",pkt_id));
+ pub_ack_rec_rel_response(client, MQTT_MSG_TYPE_PUBREL, pkt_id, 1);
+
+ } else if (pkt_type == MQTT_MSG_TYPE_PUBREL) {
+ LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_message_received: PUBREL, sending PUBCOMP response with pkt_id: %d\n",pkt_id));
+ pub_ack_rec_rel_response(client, MQTT_MSG_TYPE_PUBCOMP, pkt_id, 0);
+
+ } else if (pkt_type == MQTT_MSG_TYPE_SUBACK || pkt_type == MQTT_MSG_TYPE_UNSUBACK ||
+ pkt_type == MQTT_MSG_TYPE_PUBCOMP || pkt_type == MQTT_MSG_TYPE_PUBACK) {
+ struct mqtt_request_t *r = mqtt_take_request(&client->pend_req_queue, pkt_id);
+ if (r != NULL) {
+ LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_message_received: %s response with id %d\n", mqtt_msg_type_to_str(pkt_type), pkt_id));
+ if (pkt_type == MQTT_MSG_TYPE_SUBACK) {
+ if (length < 3) {
+ LWIP_DEBUGF(MQTT_DEBUG_WARN,("mqtt_message_received: To small SUBACK packet\n"));
+ goto out_disconnect;
+ } else {
+ mqtt_incomming_suback(r, var_hdr_payload[2]);
+ }
+ } else if (r->cb != NULL) {
+ r->cb(r->arg, ERR_OK);
+ }
+ mqtt_delete_request(r);
+ } else {
+ LWIP_DEBUGF(MQTT_DEBUG_WARN,( "mqtt_message_received: Received %s reply, with wrong pkt_id: %d\n", mqtt_msg_type_to_str(pkt_type), pkt_id));
+ }
+ } else {
+ LWIP_DEBUGF(MQTT_DEBUG_WARN,( "mqtt_message_received: Received unknown message type: %d\n", pkt_type));
+ goto out_disconnect;
+ }
+ }
+ return res;
+out_disconnect:
+ return MQTT_CONNECT_DISCONNECTED;
+}
+
+
+/**
+ * MQTT incoming message parser
+ * @param client MQTT client
+ * @param p PBUF chain of received data
+ * @return Connection status
+ */
+static mqtt_connection_status_t
+mqtt_parse_incoming(mqtt_client_t *client, struct pbuf *p)
+{
+ u16_t in_offset = 0;
+ u32_t msg_rem_len = 0;
+ u8_t fixed_hdr_idx = 0;
+ u8_t b = 0;
+
+ while (p->tot_len > in_offset) {
+ if ((fixed_hdr_idx < 2) || ((b & 0x80) != 0)) {
+
+ if (fixed_hdr_idx < client->msg_idx) {
+ b = client->rx_buffer[fixed_hdr_idx];
+ } else {
+ b = pbuf_get_at(p, in_offset++);
+ client->rx_buffer[client->msg_idx++] = b;
+ }
+ fixed_hdr_idx++;
+
+ if (fixed_hdr_idx >= 2) {
+ msg_rem_len |= (u32_t)(b & 0x7f) << ((fixed_hdr_idx - 2) * 7);
+ if ((b & 0x80) == 0) {
+ LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_parse_incoming: Remaining length after fixed header: %d\n", msg_rem_len));
+ if (msg_rem_len == 0) {
+ /* Complete message with no extra headers of payload received */
+ mqtt_message_received(client, fixed_hdr_idx, 0, 0);
+ client->msg_idx = 0;
+ fixed_hdr_idx = 0;
+ } else {
+ /* Bytes remaining in message */
+ msg_rem_len = (msg_rem_len + fixed_hdr_idx) - client->msg_idx;
+ }
+ }
+ }
+ } else {
+ u16_t cpy_len, cpy_start, buffer_space;
+
+ cpy_start = (client->msg_idx - fixed_hdr_idx) % (MQTT_VAR_HEADER_BUFFER_LEN - fixed_hdr_idx) + fixed_hdr_idx;
+
+ /* Allow to copy the lesser one of available length in input data or bytes remaining in message */
+ cpy_len = (u16_t)LWIP_MIN((u16_t)(p->tot_len - in_offset), msg_rem_len);
+
+ /* Limit to available space in buffer */
+ buffer_space = MQTT_VAR_HEADER_BUFFER_LEN - cpy_start;
+ if (cpy_len > buffer_space) {
+ cpy_len = buffer_space;
+ }
+ pbuf_copy_partial(p, client->rx_buffer+cpy_start, cpy_len, in_offset);
+
+ /* Advance get and put indexes */
+ client->msg_idx += cpy_len;
+ in_offset += cpy_len;
+ msg_rem_len -= cpy_len;
+
+ LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_parse_incoming: msg_idx: %d, cpy_len: %d, remaining %d\n", client->msg_idx, cpy_len, msg_rem_len));
+ if (msg_rem_len == 0 || cpy_len == buffer_space) {
+ /* Whole message received or buffer is full */
+ mqtt_connection_status_t res = mqtt_message_received(client, fixed_hdr_idx, (cpy_start + cpy_len) - fixed_hdr_idx, msg_rem_len);
+ if (res != MQTT_CONNECT_ACCEPTED) {
+ return res;
+ }
+ if (msg_rem_len == 0) {
+ /* Reset parser state */
+ client->msg_idx = 0;
+ /* msg_tot_len = 0; */
+ fixed_hdr_idx = 0;
+ }
+ }
+ }
+ }
+ return MQTT_CONNECT_ACCEPTED;
+}
+
+
+/**
+ * TCP received callback function. @see tcp_recv_fn
+ * @param arg MQTT client
+ * @param p PBUF chain of received data
+ * @param err Passed as return value if not ERR_OK
+ * @return ERR_OK or err passed into callback
+ */
+static err_t
+mqtt_tcp_recv_cb(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
+{
+ mqtt_client_t *client = (mqtt_client_t *)arg;
+ LWIP_ASSERT("mqtt_tcp_recv_cb: client != NULL", client != NULL);
+ LWIP_ASSERT("mqtt_tcp_recv_cb: client->conn == pcb", client->conn == pcb);
+
+ if (p == NULL) {
+ LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_tcp_recv_cb: Recv pbuf=NULL, remote has closed connection\n"));
+ mqtt_close(client, MQTT_CONNECT_DISCONNECTED);
+ } else {
+ mqtt_connection_status_t res;
+ if (err != ERR_OK) {
+ LWIP_DEBUGF(MQTT_DEBUG_WARN,("mqtt_tcp_recv_cb: Recv err=%d\n", err));
+ pbuf_free(p);
+ return err;
+ }
+
+ /* Tell remote that data has been received */
+ tcp_recved(pcb, p->tot_len);
+ res = mqtt_parse_incoming(client, p);
+ pbuf_free(p);
+
+ if (res != MQTT_CONNECT_ACCEPTED) {
+ mqtt_close(client, res);
+ }
+ /* If keep alive functionality is used */
+ if (client->keep_alive != 0) {
+ /* Reset server alive watchdog */
+ client->server_watchdog = 0;
+ }
+
+ }
+ return ERR_OK;
+}
+
+
+/**
+ * TCP data sent callback function. @see tcp_sent_fn
+ * @param arg MQTT client
+ * @param tpcb TCP connection handle
+ * @param len Number of bytes sent
+ * @return ERR_OK
+ */
+static err_t
+mqtt_tcp_sent_cb(void *arg, struct tcp_pcb *tpcb, u16_t len)
+{
+ mqtt_client_t *client = (mqtt_client_t *)arg;
+
+ LWIP_UNUSED_ARG(tpcb);
+ LWIP_UNUSED_ARG(len);
+
+ if (client->conn_state == MQTT_CONNECTED) {
+ struct mqtt_request_t *r;
+
+ /* Reset keep-alive send timer and server watchdog */
+ client->cyclic_tick = 0;
+ client->server_watchdog = 0;
+ /* QoS 0 publish has no response from server, so call its callbacks here */
+ while ((r = mqtt_take_request(&client->pend_req_queue, 0)) != NULL) {
+ LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_tcp_sent_cb: Calling QoS 0 publish complete callback\n"));
+ if (r->cb != NULL) {
+ r->cb(r->arg, ERR_OK);
+ }
+ mqtt_delete_request(r);
+ }
+ /* Try send any remaining buffers from output queue */
+ mqtt_output_send(&client->output, client->conn);
+ }
+ return ERR_OK;
+}
+
+/**
+ * TCP error callback function. @see tcp_err_fn
+ * @param arg MQTT client
+ * @param err Error encountered
+ */
+static void
+mqtt_tcp_err_cb(void *arg, err_t err)
+{
+ mqtt_client_t *client = (mqtt_client_t *)arg;
+ LWIP_UNUSED_ARG(err); /* only used for debug output */
+ LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_tcp_err_cb: TCP error callback: error %d, arg: %p\n", err, arg));
+ LWIP_ASSERT("mqtt_tcp_err_cb: client != NULL", client != NULL);
+ /* Set conn to null before calling close as pcb is already deallocated*/
+ client->conn = 0;
+ mqtt_close(client, MQTT_CONNECT_DISCONNECTED);
+}
+
+/**
+ * TCP poll callback function. @see tcp_poll_fn
+ * @param arg MQTT client
+ * @param tpcb TCP connection handle
+ * @return err ERR_OK
+ */
+static err_t
+mqtt_tcp_poll_cb(void *arg, struct tcp_pcb *tpcb)
+{
+ mqtt_client_t *client = (mqtt_client_t *)arg;
+ if (client->conn_state == MQTT_CONNECTED) {
+ /* Try send any remaining buffers from output queue */
+ mqtt_output_send(&client->output, tpcb);
+ }
+ return ERR_OK;
+}
+
+/**
+ * TCP connect callback function. @see tcp_connected_fn
+ * @param arg MQTT client
+ * @param err Always ERR_OK, mqtt_tcp_err_cb is called in case of error
+ * @return ERR_OK
+ */
+static err_t
+mqtt_tcp_connect_cb(void *arg, struct tcp_pcb *tpcb, err_t err)
+{
+ mqtt_client_t* client = (mqtt_client_t *)arg;
+
+ if (err != ERR_OK) {
+ LWIP_DEBUGF(MQTT_DEBUG_WARN,("mqtt_tcp_connect_cb: TCP connect error %d\n", err));
+ return err;
+ }
+
+ /* Initiate receiver state */
+ client->msg_idx = 0;
+
+ /* Setup TCP callbacks */
+ tcp_recv(tpcb, mqtt_tcp_recv_cb);
+ tcp_sent(tpcb, mqtt_tcp_sent_cb);
+ tcp_poll(tpcb, mqtt_tcp_poll_cb, 2);
+
+ LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_tcp_connect_cb: TCP connection established to server\n"));
+ /* Enter MQTT connect state */
+ client->conn_state = MQTT_CONNECTING;
+
+ /* Start cyclic timer */
+ sys_timeout(MQTT_CYCLIC_TIMER_INTERVAL*1000, mqtt_cyclic_timer, client);
+ client->cyclic_tick = 0;
+
+ /* Start transmission from output queue, connect message is the first one out*/
+ mqtt_output_send(&client->output, client->conn);
+
+ return ERR_OK;
+}
+
+
+
+/*---------------------------------------------------------------------------------------------------- */
+/* Public API */
+
+
+/**
+ * @ingroup mqtt
+ * MQTT publish function.
+ * @param client MQTT client
+ * @param topic Publish topic string
+ * @param payload Data to publish (NULL is allowed)
+ * @param payload_length: Length of payload (0 is allowed)
+ * @param qos Quality of service, 0 1 or 2
+ * @param retain MQTT retain flag
+ * @param cb Callback to call when publish is complete or has timed out
+ * @param arg User supplied argument to publish callback
+ * @return ERR_OK if successful
+ * ERR_CONN if client is disconnected
+ * ERR_MEM if short on memory
+ */
+err_t
+mqtt_publish(mqtt_client_t *client, const char *topic, const void *payload, u16_t payload_length, u8_t qos, u8_t retain,
+ mqtt_request_cb_t cb, void *arg)
+{
+ struct mqtt_request_t *r;
+ u16_t pkt_id;
+ size_t topic_strlen;
+ size_t total_len;
+ u16_t topic_len;
+ u16_t remaining_length;
+
+ LWIP_ASSERT("mqtt_publish: client != NULL", client);
+ LWIP_ASSERT("mqtt_publish: topic != NULL", topic);
+ LWIP_ERROR("mqtt_publish: TCP disconnected", (client->conn_state != TCP_DISCONNECTED), return ERR_CONN);
+
+ topic_strlen = strlen(topic);
+ LWIP_ERROR("mqtt_publish: topic length overflow", (topic_strlen <= (0xFFFF - 2)), return ERR_ARG);
+ topic_len = (u16_t)topic_strlen;
+ total_len = 2 + topic_len + payload_length;
+ LWIP_ERROR("mqtt_publish: total length overflow", (total_len <= 0xFFFF), return ERR_ARG);
+ remaining_length = (u16_t)total_len;
+
+ LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_publish: Publish with payload length %d to topic \"%s\"\n", payload_length, topic));
+
+ if (qos > 0) {
+ remaining_length += 2;
+ /* Generate pkt_id id for QoS1 and 2 */
+ pkt_id = msg_generate_packet_id(client);
+ } else {
+ /* Use reserved value pkt_id 0 for QoS 0 in request handle */
+ pkt_id = 0;
+ }
+
+ r = mqtt_create_request(client->req_list, pkt_id, cb, arg);
+ if (r == NULL) {
+ return ERR_MEM;
+ }
+
+ if (mqtt_output_check_space(&client->output, remaining_length) == 0) {
+ mqtt_delete_request(r);
+ return ERR_MEM;
+ }
+ /* Append fixed header */
+ mqtt_output_append_fixed_header(&client->output, MQTT_MSG_TYPE_PUBLISH, 0, qos, retain, remaining_length);
+
+ /* Append Topic */
+ mqtt_output_append_string(&client->output, topic, topic_len);
+
+ /* Append packet if for QoS 1 and 2*/
+ if (qos > 0) {
+ mqtt_output_append_u16(&client->output, pkt_id);
+ }
+
+ /* Append optional publish payload */
+ if ((payload != NULL) && (payload_length > 0)) {
+ mqtt_output_append_buf(&client->output, payload, payload_length);
+ }
+
+ mqtt_append_request(&client->pend_req_queue, r);
+ mqtt_output_send(&client->output, client->conn);
+ return ERR_OK;
+}
+
+
+/**
+ * @ingroup mqtt
+ * MQTT subscribe/unsubscribe function.
+ * @param client MQTT client
+ * @param topic topic to subscribe to
+ * @param qos Quality of service, 0 1 or 2 (only used for subscribe)
+ * @param cb Callback to call when subscribe/unsubscribe reponse is received
+ * @param arg User supplied argument to publish callback
+ * @param sub 1 for subscribe, 0 for unsubscribe
+ * @return ERR_OK if successful, @see err_t enum for other results
+ */
+err_t
+mqtt_sub_unsub(mqtt_client_t *client, const char *topic, u8_t qos, mqtt_request_cb_t cb, void *arg, u8_t sub)
+{
+ size_t topic_strlen;
+ size_t total_len;
+ u16_t topic_len;
+ u16_t remaining_length;
+ u16_t pkt_id;
+ struct mqtt_request_t *r;
+
+ LWIP_ASSERT("mqtt_sub_unsub: client != NULL", client);
+ LWIP_ASSERT("mqtt_sub_unsub: topic != NULL", topic);
+
+ topic_strlen = strlen(topic);
+ LWIP_ERROR("mqtt_sub_unsub: topic length overflow", (topic_strlen <= (0xFFFF - 2)), return ERR_ARG);
+ topic_len = (u16_t)topic_strlen;
+ /* Topic string, pkt_id, qos for subscribe */
+ total_len = topic_len + 2 + 2 + (sub != 0);
+ LWIP_ERROR("mqtt_sub_unsub: total length overflow", (total_len <= 0xFFFF), return ERR_ARG);
+ remaining_length = (u16_t)total_len;
+
+ LWIP_ASSERT("mqtt_sub_unsub: qos < 3", qos < 3);
+ if (client->conn_state == TCP_DISCONNECTED) {
+ LWIP_DEBUGF(MQTT_DEBUG_WARN,("mqtt_sub_unsub: Can not (un)subscribe in disconnected state\n"));
+ return ERR_CONN;
+ }
+
+ pkt_id = msg_generate_packet_id(client);
+ r = mqtt_create_request(client->req_list, pkt_id, cb, arg);
+ if (r == NULL) {
+ return ERR_MEM;
+ }
+
+ if (mqtt_output_check_space(&client->output, remaining_length) == 0) {
+ mqtt_delete_request(r);
+ return ERR_MEM;
+ }
+
+ LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_sub_unsub: Client (un)subscribe to topic \"%s\", id: %d\n", topic, pkt_id));
+
+ mqtt_output_append_fixed_header(&client->output, sub ? MQTT_MSG_TYPE_SUBSCRIBE : MQTT_MSG_TYPE_UNSUBSCRIBE, 0, 1, 0, remaining_length);
+ /* Packet id */
+ mqtt_output_append_u16(&client->output, pkt_id);
+ /* Topic */
+ mqtt_output_append_string(&client->output, topic, topic_len);
+ /* QoS */
+ if (sub != 0) {
+ mqtt_output_append_u8(&client->output, LWIP_MIN(qos, 2));
+ }
+
+ mqtt_append_request(&client->pend_req_queue, r);
+ mqtt_output_send(&client->output, client->conn);
+ return ERR_OK;
+}
+
+
+/**
+ * @ingroup mqtt
+ * Set callback to handle incoming publish requests from server
+ * @param client MQTT client
+ * @param pub_cb Callback invoked when publish starts, contain topic and total length of payload
+ * @param data_cb Callback for each fragment of payload that arrives
+ * @param arg User supplied argument to both callbacks
+ */
+void
+mqtt_set_inpub_callback(mqtt_client_t *client, mqtt_incoming_publish_cb_t pub_cb,
+ mqtt_incoming_data_cb_t data_cb, void *arg)
+{
+ LWIP_ASSERT("mqtt_set_inpub_callback: client != NULL", client != NULL);
+ client->data_cb = data_cb;
+ client->pub_cb = pub_cb;
+ client->inpub_arg = arg;
+}
+
+/**
+ * @ingroup mqtt
+ * Create a new MQTT client instance
+ * @return Pointer to instance on success, NULL otherwise
+ */
+mqtt_client_t *
+mqtt_client_new(void)
+{
+ mqtt_client_t *client = (mqtt_client_t *)mem_malloc(sizeof(mqtt_client_t));
+ if (client != NULL) {
+ memset(client, 0, sizeof(mqtt_client_t));
+ }
+ return client;
+}
+
+
+/**
+ * @ingroup mqtt
+ * Connect to MQTT server
+ * @param client MQTT client
+ * @param ip_addr Server IP
+ * @param port Server port
+ * @param cb Connection state change callback
+ * @param arg User supplied argument to connection callback
+ * @param client_info Client identification and connection options
+ * @return ERR_OK if successful, @see err_t enum for other results
+ */
+err_t
+mqtt_client_connect(mqtt_client_t *client, const ip_addr_t *ip_addr, u16_t port, mqtt_connection_cb_t cb, void *arg,
+ const struct mqtt_connect_client_info_t *client_info)
+{
+ err_t err;
+ size_t len;
+ u16_t client_id_length;
+ /* Length is the sum of 2+"MQTT", protocol level, flags and keep alive */
+ u16_t remaining_length = 2 + 4 + 1 + 1 + 2;
+ u8_t flags = 0, will_topic_len = 0, will_msg_len = 0;
+
+ LWIP_ASSERT("mqtt_client_connect: client != NULL", client != NULL);
+ LWIP_ASSERT("mqtt_client_connect: ip_addr != NULL", ip_addr != NULL);
+ LWIP_ASSERT("mqtt_client_connect: client_info != NULL", client_info != NULL);
+ LWIP_ASSERT("mqtt_client_connect: client_info->client_id != NULL", client_info->client_id != NULL);
+
+ if (client->conn_state != TCP_DISCONNECTED) {
+ LWIP_DEBUGF(MQTT_DEBUG_WARN,("mqtt_client_connect: Already connected\n"));
+ return ERR_ISCONN;
+ }
+
+ /* Wipe clean */
+ memset(client, 0, sizeof(mqtt_client_t));
+ client->connect_arg = arg;
+ client->connect_cb = cb;
+ client->keep_alive = client_info->keep_alive;
+ mqtt_init_requests(client->req_list);
+
+ /* Build connect message */
+ if (client_info->will_topic != NULL && client_info->will_msg != NULL) {
+ flags |= MQTT_CONNECT_FLAG_WILL;
+ flags |= (client_info->will_qos & 3) << 3;
+ if (client_info->will_retain) {
+ flags |= MQTT_CONNECT_FLAG_WILL_RETAIN;
+ }
+ len = strlen(client_info->will_topic);
+ LWIP_ERROR("mqtt_client_connect: client_info->will_topic length overflow", len <= 0xFF, return ERR_VAL);
+ LWIP_ERROR("mqtt_client_connect: client_info->will_topic length must be > 0", len > 0, return ERR_VAL);
+ will_topic_len = (u8_t)len;
+ len = strlen(client_info->will_msg);
+ LWIP_ERROR("mqtt_client_connect: client_info->will_msg length overflow", len <= 0xFF, return ERR_VAL);
+ will_msg_len = (u8_t)len;
+ len = remaining_length + 2 + will_topic_len + 2 + will_msg_len;
+ LWIP_ERROR("mqtt_client_connect: remaining_length overflow", len <= 0xFFFF, return ERR_VAL);
+ remaining_length = (u16_t)len;
+ }
+
+ /* Don't complicate things, always connect using clean session */
+ flags |= MQTT_CONNECT_FLAG_CLEAN_SESSION;
+
+ len = strlen(client_info->client_id);
+ LWIP_ERROR("mqtt_client_connect: client_info->client_id length overflow", len <= 0xFFFF, return ERR_VAL);
+ client_id_length = (u16_t)len;
+ len = remaining_length + 2 + client_id_length;
+ LWIP_ERROR("mqtt_client_connect: remaining_length overflow", len <= 0xFFFF, return ERR_VAL);
+ remaining_length = (u16_t)len;
+
+ if (mqtt_output_check_space(&client->output, remaining_length) == 0) {
+ return ERR_MEM;
+ }
+
+ client->conn = tcp_new();
+ if (client->conn == NULL) {
+ return ERR_MEM;
+ }
+
+ /* Set arg pointer for callbacks */
+ tcp_arg(client->conn, client);
+ /* Any local address, pick random local port number */
+ err = tcp_bind(client->conn, IP_ADDR_ANY, 0);
+ if (err != ERR_OK) {
+ LWIP_DEBUGF(MQTT_DEBUG_WARN,("mqtt_client_connect: Error binding to local ip/port, %d\n", err));
+ goto tcp_fail;
+ }
+ LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_client_connect: Connecting to host: %s at port:%"U16_F"\n", ipaddr_ntoa(ip_addr), port));
+
+ /* Connect to server */
+ err = tcp_connect(client->conn, ip_addr, port, mqtt_tcp_connect_cb);
+ if (err != ERR_OK) {
+ LWIP_DEBUGF(MQTT_DEBUG_TRACE,("mqtt_client_connect: Error connecting to remote ip/port, %d\n", err));
+ goto tcp_fail;
+ }
+ /* Set error callback */
+ tcp_err(client->conn, mqtt_tcp_err_cb);
+ client->conn_state = TCP_CONNECTING;
+
+ /* Append fixed header */
+ mqtt_output_append_fixed_header(&client->output, MQTT_MSG_TYPE_CONNECT, 0, 0, 0, remaining_length);
+ /* Append Protocol string */
+ mqtt_output_append_string(&client->output, "MQTT", 4);
+ /* Append Protocol level */
+ mqtt_output_append_u8(&client->output, 4);
+ /* Append connect flags */
+ mqtt_output_append_u8(&client->output, flags);
+ /* Append keep-alive */
+ mqtt_output_append_u16(&client->output, client_info->keep_alive);
+ /* Append client id */
+ mqtt_output_append_string(&client->output, client_info->client_id, client_id_length);
+ /* Append will message if used */
+ if ((flags & MQTT_CONNECT_FLAG_WILL) != 0) {
+ mqtt_output_append_string(&client->output, client_info->will_topic, will_topic_len);
+ mqtt_output_append_string(&client->output, client_info->will_msg, will_msg_len);
+ }
+ return ERR_OK;
+
+tcp_fail:
+ tcp_abort(client->conn);
+ client->conn = NULL;
+ return err;
+}
+
+
+/**
+ * @ingroup mqtt
+ * Disconnect from MQTT server
+ * @param client MQTT client
+ */
+void
+mqtt_disconnect(mqtt_client_t *client)
+{
+ LWIP_ASSERT("mqtt_disconnect: client != NULL", client);
+ /* If connection in not already closed */
+ if (client->conn_state != TCP_DISCONNECTED) {
+ /* Set conn_state before calling mqtt_close to prevent callback from being called */
+ client->conn_state = TCP_DISCONNECTED;
+ mqtt_close(client, (mqtt_connection_status_t)0);
+ }
+}
+
+/**
+ * @ingroup mqtt
+ * Check connection with server
+ * @param client MQTT client
+ * @return 1 if connected to server, 0 otherwise
+ */
+u8_t
+mqtt_client_is_connected(mqtt_client_t *client)
+{
+ LWIP_ASSERT("mqtt_client_is_connected: client != NULL", client);
+ return client->conn_state == MQTT_CONNECTED;
+}
+
+#endif /* LWIP_TCP && LWIP_CALLBACK_API */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/netbiosns/netbiosns.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/netbiosns/netbiosns.c
new file mode 100644
index 0000000..2dfbe65
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/netbiosns/netbiosns.c
@@ -0,0 +1,367 @@
+/**
+ * @file
+ * NetBIOS name service responder
+ */
+
+/**
+ * @defgroup netbiosns NETBIOS responder
+ * @ingroup apps
+ *
+ * This is an example implementation of a NetBIOS name server.
+ * It responds to name queries for a configurable name.
+ * Name resolving is not supported.
+ *
+ * Note that the device doesn't broadcast it's own name so can't
+ * detect duplicate names!
+ */
+
+/*
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ */
+
+#include "lwip/apps/netbiosns.h"
+
+#if LWIP_IPV4 && LWIP_UDP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/def.h"
+#include "lwip/udp.h"
+#include "lwip/netif.h"
+
+#include <string.h>
+
+/** default port number for "NetBIOS Name service */
+#define NETBIOS_PORT 137
+
+/** size of a NetBIOS name */
+#define NETBIOS_NAME_LEN 16
+
+/** The Time-To-Live for NetBIOS name responds (in seconds)
+ * Default is 300000 seconds (3 days, 11 hours, 20 minutes) */
+#define NETBIOS_NAME_TTL 300000u
+
+/** NetBIOS header flags */
+#define NETB_HFLAG_RESPONSE 0x8000U
+#define NETB_HFLAG_OPCODE 0x7800U
+#define NETB_HFLAG_OPCODE_NAME_QUERY 0x0000U
+#define NETB_HFLAG_AUTHORATIVE 0x0400U
+#define NETB_HFLAG_TRUNCATED 0x0200U
+#define NETB_HFLAG_RECURS_DESIRED 0x0100U
+#define NETB_HFLAG_RECURS_AVAILABLE 0x0080U
+#define NETB_HFLAG_BROADCAST 0x0010U
+#define NETB_HFLAG_REPLYCODE 0x0008U
+#define NETB_HFLAG_REPLYCODE_NOERROR 0x0000U
+
+/** NetBIOS name flags */
+#define NETB_NFLAG_UNIQUE 0x8000U
+#define NETB_NFLAG_NODETYPE 0x6000U
+#define NETB_NFLAG_NODETYPE_HNODE 0x6000U
+#define NETB_NFLAG_NODETYPE_MNODE 0x4000U
+#define NETB_NFLAG_NODETYPE_PNODE 0x2000U
+#define NETB_NFLAG_NODETYPE_BNODE 0x0000U
+
+/** NetBIOS message header */
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct netbios_hdr {
+ PACK_STRUCT_FIELD(u16_t trans_id);
+ PACK_STRUCT_FIELD(u16_t flags);
+ PACK_STRUCT_FIELD(u16_t questions);
+ PACK_STRUCT_FIELD(u16_t answerRRs);
+ PACK_STRUCT_FIELD(u16_t authorityRRs);
+ PACK_STRUCT_FIELD(u16_t additionalRRs);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+/** NetBIOS message name part */
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct netbios_name_hdr {
+ PACK_STRUCT_FLD_8(u8_t nametype);
+ PACK_STRUCT_FLD_8(u8_t encname[(NETBIOS_NAME_LEN*2)+1]);
+ PACK_STRUCT_FIELD(u16_t type);
+ PACK_STRUCT_FIELD(u16_t cls);
+ PACK_STRUCT_FIELD(u32_t ttl);
+ PACK_STRUCT_FIELD(u16_t datalen);
+ PACK_STRUCT_FIELD(u16_t flags);
+ PACK_STRUCT_FLD_S(ip4_addr_p_t addr);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+/** NetBIOS message */
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct netbios_resp
+{
+ struct netbios_hdr resp_hdr;
+ struct netbios_name_hdr resp_name;
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+#ifdef NETBIOS_LWIP_NAME
+#define NETBIOS_LOCAL_NAME NETBIOS_LWIP_NAME
+#else
+static char netbiosns_local_name[NETBIOS_NAME_LEN];
+#define NETBIOS_LOCAL_NAME netbiosns_local_name
+#endif
+
+struct udp_pcb *netbiosns_pcb;
+
+/** Decode a NetBIOS name (from packet to string) */
+static int
+netbiosns_name_decode(char *name_enc, char *name_dec, int name_dec_len)
+{
+ char *pname;
+ char cname;
+ char cnbname;
+ int idx = 0;
+
+ LWIP_UNUSED_ARG(name_dec_len);
+
+ /* Start decoding netbios name. */
+ pname = name_enc;
+ for (;;) {
+ /* Every two characters of the first level-encoded name
+ * turn into one character in the decoded name. */
+ cname = *pname;
+ if (cname == '\0')
+ break; /* no more characters */
+ if (cname == '.')
+ break; /* scope ID follows */
+ if (cname < 'A' || cname > 'Z') {
+ /* Not legal. */
+ return -1;
+ }
+ cname -= 'A';
+ cnbname = cname << 4;
+ pname++;
+
+ cname = *pname;
+ if (cname == '\0' || cname == '.') {
+ /* No more characters in the name - but we're in
+ * the middle of a pair. Not legal. */
+ return -1;
+ }
+ if (cname < 'A' || cname > 'Z') {
+ /* Not legal. */
+ return -1;
+ }
+ cname -= 'A';
+ cnbname |= cname;
+ pname++;
+
+ /* Do we have room to store the character? */
+ if (idx < NETBIOS_NAME_LEN) {
+ /* Yes - store the character. */
+ name_dec[idx++] = (cnbname!=' '?cnbname:'\0');
+ }
+ }
+
+ return 0;
+}
+
+#if 0 /* function currently unused */
+/** Encode a NetBIOS name (from string to packet) - currently unused because
+ we don't ask for names. */
+static int
+netbiosns_name_encode(char *name_enc, char *name_dec, int name_dec_len)
+{
+ char *pname;
+ char cname;
+ unsigned char ucname;
+ int idx = 0;
+
+ /* Start encoding netbios name. */
+ pname = name_enc;
+
+ for (;;) {
+ /* Every two characters of the first level-encoded name
+ * turn into one character in the decoded name. */
+ cname = *pname;
+ if (cname == '\0')
+ break; /* no more characters */
+ if (cname == '.')
+ break; /* scope ID follows */
+ if ((cname < 'A' || cname > 'Z') && (cname < '0' || cname > '9')) {
+ /* Not legal. */
+ return -1;
+ }
+
+ /* Do we have room to store the character? */
+ if (idx >= name_dec_len) {
+ return -1;
+ }
+
+ /* Yes - store the character. */
+ ucname = cname;
+ name_dec[idx++] = ('A'+((ucname>>4) & 0x0F));
+ name_dec[idx++] = ('A'+( ucname & 0x0F));
+ pname++;
+ }
+
+ /* Fill with "space" coding */
+ for (;idx < name_dec_len - 1;) {
+ name_dec[idx++] = 'C';
+ name_dec[idx++] = 'A';
+ }
+
+ /* Terminate string */
+ name_dec[idx] = '\0';
+
+ return 0;
+}
+#endif /* 0 */
+
+/** NetBIOS Name service recv callback */
+static void
+netbiosns_recv(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
+{
+ LWIP_UNUSED_ARG(arg);
+
+ /* if packet is valid */
+ if (p != NULL) {
+ char netbios_name[NETBIOS_NAME_LEN+1];
+ struct netbios_hdr* netbios_hdr = (struct netbios_hdr*)p->payload;
+ struct netbios_name_hdr* netbios_name_hdr = (struct netbios_name_hdr*)(netbios_hdr+1);
+
+ /* we only answer if we got a default interface */
+ if (netif_default != NULL) {
+ /* @todo: do we need to check answerRRs/authorityRRs/additionalRRs? */
+ /* if the packet is a NetBIOS name query question */
+ if (((netbios_hdr->flags & PP_NTOHS(NETB_HFLAG_OPCODE)) == PP_NTOHS(NETB_HFLAG_OPCODE_NAME_QUERY)) &&
+ ((netbios_hdr->flags & PP_NTOHS(NETB_HFLAG_RESPONSE)) == 0) &&
+ (netbios_hdr->questions == PP_NTOHS(1))) {
+ /* decode the NetBIOS name */
+ netbiosns_name_decode((char*)(netbios_name_hdr->encname), netbios_name, sizeof(netbios_name));
+ /* if the packet is for us */
+ if (lwip_strnicmp(netbios_name, NETBIOS_LOCAL_NAME, sizeof(NETBIOS_LOCAL_NAME)) == 0) {
+ struct pbuf *q;
+ struct netbios_resp *resp;
+
+ q = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct netbios_resp), PBUF_RAM);
+ if (q != NULL) {
+ resp = (struct netbios_resp*)q->payload;
+
+ /* prepare NetBIOS header response */
+ resp->resp_hdr.trans_id = netbios_hdr->trans_id;
+ resp->resp_hdr.flags = PP_HTONS(NETB_HFLAG_RESPONSE |
+ NETB_HFLAG_OPCODE_NAME_QUERY |
+ NETB_HFLAG_AUTHORATIVE |
+ NETB_HFLAG_RECURS_DESIRED);
+ resp->resp_hdr.questions = 0;
+ resp->resp_hdr.answerRRs = PP_HTONS(1);
+ resp->resp_hdr.authorityRRs = 0;
+ resp->resp_hdr.additionalRRs = 0;
+
+ /* prepare NetBIOS header datas */
+ MEMCPY( resp->resp_name.encname, netbios_name_hdr->encname, sizeof(netbios_name_hdr->encname));
+ resp->resp_name.nametype = netbios_name_hdr->nametype;
+ resp->resp_name.type = netbios_name_hdr->type;
+ resp->resp_name.cls = netbios_name_hdr->cls;
+ resp->resp_name.ttl = PP_HTONL(NETBIOS_NAME_TTL);
+ resp->resp_name.datalen = PP_HTONS(sizeof(resp->resp_name.flags)+sizeof(resp->resp_name.addr));
+ resp->resp_name.flags = PP_HTONS(NETB_NFLAG_NODETYPE_BNODE);
+ ip4_addr_copy(resp->resp_name.addr, *netif_ip4_addr(netif_default));
+
+ /* send the NetBIOS response */
+ udp_sendto(upcb, q, addr, port);
+
+ /* free the "reference" pbuf */
+ pbuf_free(q);
+ }
+ }
+ }
+ }
+ /* free the pbuf */
+ pbuf_free(p);
+ }
+}
+
+/**
+ * @ingroup netbiosns
+ * Init netbios responder
+ */
+void
+netbiosns_init(void)
+{
+#ifdef NETBIOS_LWIP_NAME
+ LWIP_ASSERT("NetBIOS name is too long!", strlen(NETBIOS_LWIP_NAME) < NETBIOS_NAME_LEN);
+#endif
+
+ netbiosns_pcb = udp_new_ip_type(IPADDR_TYPE_ANY);
+ if (netbiosns_pcb != NULL) {
+ /* we have to be allowed to send broadcast packets! */
+ ip_set_option(netbiosns_pcb, SOF_BROADCAST);
+ udp_bind(netbiosns_pcb, IP_ANY_TYPE, NETBIOS_PORT);
+ udp_recv(netbiosns_pcb, netbiosns_recv, netbiosns_pcb);
+ }
+}
+
+#ifndef NETBIOS_LWIP_NAME
+/**
+ * @ingroup netbiosns
+ * Set netbios name. ATTENTION: the hostname must be less than 15 characters!
+ */
+void
+netbiosns_set_name(const char* hostname)
+{
+ size_t copy_len = strlen(hostname);
+ LWIP_ASSERT("NetBIOS name is too long!", copy_len < NETBIOS_NAME_LEN);
+ if (copy_len >= NETBIOS_NAME_LEN) {
+ copy_len = NETBIOS_NAME_LEN - 1;
+ }
+ MEMCPY(netbiosns_local_name, hostname, copy_len + 1);
+}
+#endif
+
+/**
+ * @ingroup netbiosns
+ * Stop netbios responder
+ */
+void
+netbiosns_stop(void)
+{
+ if (netbiosns_pcb != NULL) {
+ udp_remove(netbiosns_pcb);
+ netbiosns_pcb = NULL;
+ }
+}
+
+#endif /* LWIP_IPV4 && LWIP_UDP */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_asn1.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_asn1.c
new file mode 100644
index 0000000..f35b760
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_asn1.c
@@ -0,0 +1,749 @@
+/**
+ * @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>
+ * Martin Hentschel <info@cl-soft.de>
+ */
+
+#include "lwip/apps/snmp_opts.h"
+
+#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
+
+#include "snmp_asn1.h"
+
+#define PBUF_OP_EXEC(code) \
+ if ((code) != ERR_OK) { \
+ return ERR_BUF; \
+ }
+
+/**
+ * Encodes a TLV into a pbuf stream.
+ *
+ * @param pbuf_stream points to a pbuf stream
+ * @param tlv TLV to encode
+ * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode
+ */
+err_t
+snmp_ans1_enc_tlv(struct snmp_pbuf_stream* pbuf_stream, struct snmp_asn1_tlv* tlv)
+{
+ u8_t data;
+ u8_t length_bytes_required;
+
+ /* write type */
+ if ((tlv->type & SNMP_ASN1_DATATYPE_MASK) == SNMP_ASN1_DATATYPE_EXTENDED) {
+ /* extended format is not used by SNMP so we do not accept those values */
+ return ERR_ARG;
+ }
+ if (tlv->type_len != 0) {
+ /* any other value as auto is not accepted for type (we always use one byte because extended syntax is prohibited) */
+ return ERR_ARG;
+ }
+
+ PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, tlv->type));
+ tlv->type_len = 1;
+
+ /* write length */
+ if (tlv->value_len <= 127) {
+ length_bytes_required = 1;
+ } else if (tlv->value_len <= 255) {
+ length_bytes_required = 2;
+ } else {
+ length_bytes_required = 3;
+ }
+
+ /* check for forced min length */
+ if (tlv->length_len > 0) {
+ if (tlv->length_len < length_bytes_required) {
+ /* unable to code requested length in requested number of bytes */
+ return ERR_ARG;
+ }
+
+ length_bytes_required = tlv->length_len;
+ } else {
+ tlv->length_len = length_bytes_required;
+ }
+
+ if (length_bytes_required > 1) {
+ /* multi byte representation required */
+ length_bytes_required--;
+ data = 0x80 | length_bytes_required; /* extended length definition, 1 length byte follows */
+
+ PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, data));
+
+ while (length_bytes_required > 1) {
+ if (length_bytes_required == 2) {
+ /* append high byte */
+ data = (u8_t)(tlv->value_len >> 8);
+ } else {
+ /* append leading 0x00 */
+ data = 0x00;
+ }
+
+ PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, data));
+ length_bytes_required--;
+ }
+ }
+
+ /* append low byte */
+ data = (u8_t)(tlv->value_len & 0xFF);
+ PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, data));
+
+ return ERR_OK;
+}
+
+/**
+ * Encodes raw data (octet string, opaque) into a pbuf chained ASN1 msg.
+ *
+ * @param pbuf_stream points to a pbuf stream
+ * @param raw_len raw data length
+ * @param raw points raw data
+ * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode
+ */
+err_t
+snmp_asn1_enc_raw(struct snmp_pbuf_stream* pbuf_stream, const u8_t *raw, u16_t raw_len)
+{
+ PBUF_OP_EXEC(snmp_pbuf_stream_writebuf(pbuf_stream, raw, raw_len));
+
+ return ERR_OK;
+}
+
+/**
+ * Encodes u32_t (counter, gauge, timeticks) into a pbuf chained ASN1 msg.
+ *
+ * @param pbuf_stream points to a pbuf stream
+ * @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 successful, ERR_ARG if we can't (or won't) encode
+ *
+ * @see snmp_asn1_enc_u32t_cnt()
+ */
+err_t
+snmp_asn1_enc_u32t(struct snmp_pbuf_stream* pbuf_stream, u16_t octets_needed, u32_t value)
+{
+ if (octets_needed > 5) {
+ return ERR_ARG;
+ }
+ if (octets_needed == 5) {
+ /* not enough bits in 'value' add leading 0x00 */
+ PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, 0x00));
+ octets_needed--;
+ }
+
+ while (octets_needed > 1) {
+ octets_needed--;
+ PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(value >> (octets_needed << 3))));
+ }
+
+ /* (only) one least significant octet */
+ PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)value));
+
+ return ERR_OK;
+}
+
+/**
+ * Encodes u64_t (counter64) into a pbuf chained ASN1 msg.
+ *
+ * @param pbuf_stream points to a pbuf stream
+ * @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 successful, ERR_ARG if we can't (or won't) encode
+ *
+ * @see snmp_asn1_enc_u64t_cnt()
+ */
+err_t
+snmp_asn1_enc_u64t(struct snmp_pbuf_stream* pbuf_stream, u16_t octets_needed, const u32_t* value)
+{
+ if (octets_needed > 9) {
+ return ERR_ARG;
+ }
+ if (octets_needed == 9) {
+ /* not enough bits in 'value' add leading 0x00 */
+ PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, 0x00));
+ octets_needed--;
+ }
+
+ while (octets_needed > 4) {
+ octets_needed--;
+ PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(*value >> ((octets_needed-4) << 3))));
+ }
+
+ /* skip to low u32 */
+ value++;
+
+ while (octets_needed > 1) {
+ octets_needed--;
+ PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(*value >> (octets_needed << 3))));
+ }
+
+ /* always write at least one octet (also in case of value == 0) */
+ PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(*value)));
+
+ return ERR_OK;
+}
+
+/**
+ * Encodes s32_t integer into a pbuf chained ASN1 msg.
+ *
+ * @param pbuf_stream points to a pbuf stream
+ * @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 successful, ERR_ARG if we can't (or won't) encode
+ *
+ * @see snmp_asn1_enc_s32t_cnt()
+ */
+err_t
+snmp_asn1_enc_s32t(struct snmp_pbuf_stream* pbuf_stream, u16_t octets_needed, s32_t value)
+{
+ while (octets_needed > 1) {
+ octets_needed--;
+
+ PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(value >> (octets_needed << 3))));
+ }
+
+ /* (only) one least significant octet */
+ PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)value));
+
+ return ERR_OK;
+}
+
+/**
+ * Encodes object identifier into a pbuf chained ASN1 msg.
+ *
+ * @param pbuf_stream points to a pbuf stream
+ * @param oid points to object identifier array
+ * @param oid_len object identifier array length
+ * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode
+ */
+err_t
+snmp_asn1_enc_oid(struct snmp_pbuf_stream* pbuf_stream, const u32_t *oid, u16_t oid_len)
+{
+ if (oid_len > 1) {
+ /* write compressed first two sub id's */
+ u32_t compressed_byte = ((oid[0] * 40) + oid[1]);
+ PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)compressed_byte));
+ oid_len -= 2;
+ oid += 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 (oid_len > 0) {
+ u32_t sub_id;
+ u8_t shift, tail;
+
+ oid_len--;
+ sub_id = *oid;
+ tail = 0;
+ shift = 28;
+ while (shift > 0) {
+ u8_t code;
+
+ code = (u8_t)(sub_id >> shift);
+ if ((code != 0) || (tail != 0)) {
+ tail = 1;
+ PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, code | 0x80));
+ }
+ shift -= 7;
+ }
+ PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)sub_id & 0x7F));
+
+ /* proceed to next sub-identifier */
+ oid++;
+ }
+ return ERR_OK;
+}
+
+/**
+ * Returns octet count for length.
+ *
+ * @param length parameter 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 value to be encoded
+ * @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 u64_t.
+ *
+ * @param value value to be encoded
+ * @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_u64t_cnt(const u32_t *value, u16_t *octets_needed)
+{
+ /* check if high u32 is 0 */
+ if (*value == 0x00) {
+ /* only low u32 is important */
+ value++;
+ snmp_asn1_enc_u32t_cnt(*value, octets_needed);
+ } else {
+ /* low u32 does not matter for length determination */
+ snmp_asn1_enc_u32t_cnt(*value, octets_needed);
+ *octets_needed = *octets_needed + 4; /* add the 4 bytes of low u32 */
+ }
+}
+
+/**
+ * Returns octet count for an s32_t.
+ *
+ * @param value value to be encoded
+ * @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 oid points to object identifier array
+ * @param oid_len object identifier array length
+ * @param octets_needed points to the return value
+ */
+void
+snmp_asn1_enc_oid_cnt(const u32_t *oid, u16_t oid_len, u16_t *octets_needed)
+{
+ u32_t sub_id;
+
+ *octets_needed = 0;
+ if (oid_len > 1) {
+ /* compressed prefix in one octet */
+ (*octets_needed)++;
+ oid_len -= 2;
+ oid += 2;
+ }
+ while (oid_len > 0) {
+ oid_len--;
+ sub_id = *oid;
+
+ sub_id >>= 7;
+ (*octets_needed)++;
+ while (sub_id > 0) {
+ sub_id >>= 7;
+ (*octets_needed)++;
+ }
+ oid++;
+ }
+}
+
+/**
+ * Decodes a TLV from a pbuf stream.
+ *
+ * @param pbuf_stream points to a pbuf stream
+ * @param tlv returns decoded TLV
+ * @return ERR_OK if successful, ERR_VAL if we can't decode
+ */
+err_t
+snmp_asn1_dec_tlv(struct snmp_pbuf_stream* pbuf_stream, struct snmp_asn1_tlv* tlv)
+{
+ u8_t data;
+
+ /* decode type first */
+ PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
+ tlv->type = data;
+
+ if ((tlv->type & SNMP_ASN1_DATATYPE_MASK) == SNMP_ASN1_DATATYPE_EXTENDED) {
+ /* extended format is not used by SNMP so we do not accept those values */
+ return ERR_VAL;
+ }
+ tlv->type_len = 1;
+
+ /* now, decode length */
+ PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
+
+ if (data < 0x80) { /* short form */
+ tlv->length_len = 1;
+ tlv->value_len = data;
+ } else if (data > 0x80) { /* long form */
+ u8_t length_bytes = data - 0x80;
+ tlv->length_len = length_bytes + 1; /* this byte + defined number of length bytes following */
+ tlv->value_len = 0;
+
+ while (length_bytes > 0) {
+ /* we only support up to u16.maxvalue-1 (2 bytes) but have to accept leading zero bytes */
+ if (tlv->value_len > 0xFF) {
+ return ERR_VAL;
+ }
+ PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
+ tlv->value_len <<= 8;
+ tlv->value_len |= data;
+
+ /* take care for special value used for indefinite length */
+ if (tlv->value_len == 0xFFFF) {
+ return ERR_VAL;
+ }
+
+ length_bytes--;
+ }
+ } else { /* data == 0x80 indefinite length form */
+ /* (not allowed for SNMP; RFC 1157, 3.2.2) */
+ return ERR_VAL;
+ }
+
+ return ERR_OK;
+}
+
+/**
+ * Decodes positive integer (counter, gauge, timeticks) into u32_t.
+ *
+ * @param pbuf_stream points to a pbuf stream
+ * @param len length of the coded integer field
+ * @param value return host order integer
+ * @return ERR_OK if successful, 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 snmp_pbuf_stream *pbuf_stream, u16_t len, u32_t *value)
+{
+ u8_t data;
+
+ if ((len > 0) && (len <= 5)) {
+ PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
+
+ /* expecting sign bit to be zero, only unsigned please! */
+ if (((len == 5) && (data == 0x00)) || ((len < 5) && ((data & 0x80) == 0))) {
+ *value = data;
+ len--;
+
+ while (len > 0) {
+ PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
+ len--;
+
+ *value <<= 8;
+ *value |= data;
+ }
+
+ return ERR_OK;
+ }
+ }
+
+ return ERR_VAL;
+}
+
+/**
+ * Decodes large positive integer (counter64) into 2x u32_t.
+ *
+ * @param pbuf_stream points to a pbuf stream
+ * @param len length of the coded integer field
+ * @param value return host order integer
+ * @return ERR_OK if successful, 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_u64t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u32_t *value)
+{
+ u8_t data;
+
+ if (len <= 4) {
+ /* high u32 is 0 */
+ *value = 0;
+ /* directly skip to low u32 */
+ value++;
+ }
+
+ if ((len > 0) && (len <= 9)) {
+ PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
+
+ /* expecting sign bit to be zero, only unsigned please! */
+ if (((len == 9) && (data == 0x00)) || ((len < 9) && ((data & 0x80) == 0))) {
+ *value = data;
+ len--;
+
+ while (len > 0) {
+ PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
+
+ if (len == 4) {
+ /* skip to low u32 */
+ value++;
+ *value = 0;
+ } else {
+ *value <<= 8;
+ }
+
+ *value |= data;
+ len--;
+ }
+
+ return ERR_OK;
+ }
+ }
+
+ return ERR_VAL;
+}
+
+/**
+ * Decodes integer into s32_t.
+ *
+ * @param pbuf_stream points to a pbuf stream
+ * @param len length of the coded integer field
+ * @param value return host order integer
+ * @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode
+ *
+ * @note ASN coded integers are _always_ signed!
+ */
+err_t
+snmp_asn1_dec_s32t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, s32_t *value)
+{
+#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;
+ u8_t data;
+
+ if ((len > 0) && (len < 5)) {
+ PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
+ len--;
+
+ if (data & 0x80) {
+ /* negative, start from -1 */
+ *value = -1;
+ sign = 1;
+ *lsb_ptr &= data;
+ } else {
+ /* positive, start from 0 */
+ *value = 0;
+ sign = 0;
+ *lsb_ptr |= data;
+ }
+
+ /* OR/AND octets with value */
+ while (len > 0) {
+ PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
+ len--;
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+ *value <<= 8;
+#endif
+#if BYTE_ORDER == BIG_ENDIAN
+ *value >>= 8;
+#endif
+
+ if (sign) {
+ *lsb_ptr |= 255;
+ *lsb_ptr &= data;
+ } else {
+ *lsb_ptr |= data;
+ }
+ }
+
+ return ERR_OK;
+ }
+
+ return ERR_VAL;
+}
+
+/**
+ * Decodes object identifier from incoming message into array of u32_t.
+ *
+ * @param pbuf_stream points to a pbuf stream
+ * @param len length of the coded object identifier
+ * @param oid return decoded object identifier
+ * @param oid_len return decoded object identifier length
+ * @param oid_max_len size of oid buffer
+ * @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode
+ */
+err_t
+snmp_asn1_dec_oid(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u32_t* oid, u8_t* oid_len, u8_t oid_max_len)
+{
+ u32_t *oid_ptr;
+ u8_t data;
+
+ *oid_len = 0;
+ oid_ptr = oid;
+ if (len > 0) {
+ if (oid_max_len < 2) {
+ return ERR_MEM;
+ }
+
+ PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
+ len--;
+
+ /* first compressed octet */
+ if (data == 0x2B) {
+ /* (most) common case 1.3 (iso.org) */
+ *oid_ptr = 1;
+ oid_ptr++;
+ *oid_ptr = 3;
+ oid_ptr++;
+ } else if (data < 40) {
+ *oid_ptr = 0;
+ oid_ptr++;
+ *oid_ptr = data;
+ oid_ptr++;
+ } else if (data < 80) {
+ *oid_ptr = 1;
+ oid_ptr++;
+ *oid_ptr = data - 40;
+ oid_ptr++;
+ } else {
+ *oid_ptr = 2;
+ oid_ptr++;
+ *oid_ptr = data - 80;
+ oid_ptr++;
+ }
+ *oid_len = 2;
+ } else {
+ /* accepting zero length identifiers e.g. for getnext operation. uncommon but valid */
+ return ERR_OK;
+ }
+
+ while ((len > 0) && (*oid_len < oid_max_len)) {
+ PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
+ len--;
+
+ if ((data & 0x80) == 0x00) {
+ /* sub-identifier uses single octet */
+ *oid_ptr = data;
+ } else {
+ /* sub-identifier uses multiple octets */
+ u32_t sub_id = (data & ~0x80);
+ while ((len > 0) && ((data & 0x80) != 0)) {
+ PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
+ len--;
+
+ sub_id = (sub_id << 7) + (data & ~0x80);
+ }
+
+ if ((data & 0x80) != 0) {
+ /* "more bytes following" bit still set at end of len */
+ return ERR_VAL;
+ }
+ *oid_ptr = sub_id;
+ }
+ oid_ptr++;
+ (*oid_len)++;
+ }
+
+ if (len > 0) {
+ /* OID to long to fit in our buffer */
+ return ERR_MEM;
+ }
+
+ return ERR_OK;
+}
+
+/**
+ * Decodes (copies) raw data (ip-addresses, octet strings, opaque encoding)
+ * from incoming message into array.
+ *
+ * @param pbuf_stream points to a pbuf stream
+ * @param len length of the coded raw data (zero is valid, e.g. empty string!)
+ * @param buf return raw bytes
+ * @param buf_len returns length of the raw return value
+ * @param buf_max_len buffer size
+ * @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode
+ */
+err_t
+snmp_asn1_dec_raw(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u8_t *buf, u16_t* buf_len, u16_t buf_max_len)
+{
+ if (len > buf_max_len) {
+ /* not enough dst space */
+ return ERR_MEM;
+ }
+ *buf_len = len;
+
+ while (len > 0) {
+ PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, buf));
+ buf++;
+ len--;
+ }
+
+ return ERR_OK;
+}
+
+#endif /* LWIP_SNMP */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_asn1.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_asn1.h
new file mode 100644
index 0000000..ec50d8c
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_asn1.h
@@ -0,0 +1,108 @@
+/**
+ * @file
+ * Abstract Syntax Notation One (ISO 8824, 8825) codec.
+ */
+
+/*
+ * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
+ * Copyright (c) 2016 Elias Oenal.
+ * 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>
+ * Martin Hentschel <info@cl-soft.de>
+ * Elias Oenal <lwip@eliasoenal.com>
+ */
+
+#ifndef LWIP_HDR_APPS_SNMP_ASN1_H
+#define LWIP_HDR_APPS_SNMP_ASN1_H
+
+#include "lwip/apps/snmp_opts.h"
+
+#if LWIP_SNMP
+
+#include "lwip/err.h"
+#include "lwip/apps/snmp_core.h"
+#include "snmp_pbuf_stream.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define SNMP_ASN1_TLV_INDEFINITE_LENGTH 0x80
+
+#define SNMP_ASN1_CLASS_MASK 0xC0
+#define SNMP_ASN1_CONTENTTYPE_MASK 0x20
+#define SNMP_ASN1_DATATYPE_MASK 0x1F
+#define SNMP_ASN1_DATATYPE_EXTENDED 0x1F /* DataType indicating that datatype is encoded in following bytes */
+
+/* context specific (SNMP) tags (from SNMP spec. RFC1157) */
+#define SNMP_ASN1_CONTEXT_PDU_GET_REQ 0
+#define SNMP_ASN1_CONTEXT_PDU_GET_NEXT_REQ 1
+#define SNMP_ASN1_CONTEXT_PDU_GET_RESP 2
+#define SNMP_ASN1_CONTEXT_PDU_SET_REQ 3
+#define SNMP_ASN1_CONTEXT_PDU_TRAP 4
+#define SNMP_ASN1_CONTEXT_PDU_GET_BULK_REQ 5
+
+#define SNMP_ASN1_CONTEXT_VARBIND_NO_SUCH_OBJECT 0
+#define SNMP_ASN1_CONTEXT_VARBIND_END_OF_MIB_VIEW 2
+
+struct snmp_asn1_tlv
+{
+ u8_t type; /* only U8 because extended types are not specified by SNMP */
+ u8_t type_len; /* encoded length of 'type' field (normally 1) */
+ u8_t length_len; /* indicates how many bytes are required to encode the 'value_len' field */
+ u16_t value_len; /* encoded length of the value */
+};
+#define SNMP_ASN1_TLV_HDR_LENGTH(tlv) ((tlv).type_len + (tlv).length_len)
+#define SNMP_ASN1_TLV_LENGTH(tlv) ((tlv).type_len + (tlv).length_len + (tlv).value_len)
+#define SNMP_ASN1_SET_TLV_PARAMS(tlv, type_, length_len_, value_len_) do { (tlv).type = (type_); (tlv).type_len = 0; (tlv).length_len = (length_len_); (tlv).value_len = (value_len_); } while (0);
+
+err_t snmp_asn1_dec_tlv(struct snmp_pbuf_stream* pbuf_stream, struct snmp_asn1_tlv* tlv);
+err_t snmp_asn1_dec_u32t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u32_t *value);
+err_t snmp_asn1_dec_u64t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u32_t *value);
+err_t snmp_asn1_dec_s32t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, s32_t *value);
+err_t snmp_asn1_dec_oid(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u32_t* oid, u8_t* oid_len, u8_t oid_max_len);
+err_t snmp_asn1_dec_raw(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u8_t *buf, u16_t* buf_len, u16_t buf_max_len);
+
+err_t snmp_ans1_enc_tlv(struct snmp_pbuf_stream* pbuf_stream, struct snmp_asn1_tlv* tlv);
+
+void snmp_asn1_enc_length_cnt(u16_t length, u8_t *octets_needed);
+void snmp_asn1_enc_u32t_cnt(u32_t value, u16_t *octets_needed);
+void snmp_asn1_enc_u64t_cnt(const u32_t *value, u16_t *octets_needed);
+void snmp_asn1_enc_s32t_cnt(s32_t value, u16_t *octets_needed);
+void snmp_asn1_enc_oid_cnt(const u32_t *oid, u16_t oid_len, u16_t *octets_needed);
+err_t snmp_asn1_enc_oid(struct snmp_pbuf_stream* pbuf_stream, const u32_t *oid, u16_t oid_len);
+err_t snmp_asn1_enc_s32t(struct snmp_pbuf_stream* pbuf_stream, u16_t octets_needed, s32_t value);
+err_t snmp_asn1_enc_u32t(struct snmp_pbuf_stream* pbuf_stream, u16_t octets_needed, u32_t value);
+err_t snmp_asn1_enc_u64t(struct snmp_pbuf_stream* pbuf_stream, u16_t octets_needed, const u32_t* value);
+err_t snmp_asn1_enc_raw(struct snmp_pbuf_stream* pbuf_stream, const u8_t *raw, u16_t raw_len);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_SNMP */
+
+#endif /* LWIP_HDR_APPS_SNMP_ASN1_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_core.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_core.c
new file mode 100644
index 0000000..c041833
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_core.c
@@ -0,0 +1,1349 @@
+/**
+ * @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>
+ * Martin Hentschel <info@cl-soft.de>
+*/
+
+/**
+ * @defgroup snmp SNMPv2c agent
+ * @ingroup apps
+ * SNMPv2c compatible agent\n
+ * There is also a MIB compiler and a MIB viewer in lwIP contrib repository
+ * (lwip-contrib/apps/LwipMibCompiler).\n
+ * The agent implements the most important MIB2 MIBs including IPv6 support
+ * (interfaces, UDP, TCP, SNMP, ICMP, SYSTEM). IP MIB is an older version
+ * whithout IPv6 statistics (TODO).\n
+ * Rewritten by Martin Hentschel <info@cl-soft.de> and
+ * Dirk Ziegelmeier <dziegel@gmx.de>\n
+ * Work on SNMPv3 has started, but is not finished.\n
+ *
+ * 0 Agent Capabilities
+ * ====================
+ *
+ * Features:
+ * ---------
+ * - SNMPv2c support.
+ * - Low RAM usage - no memory pools, stack only.
+ * - MIB2 implementation is separated from SNMP stack.
+ * - Support for multiple MIBs (snmp_set_mibs() call) - e.g. for private MIB.
+ * - Simple and generic API for MIB implementation.
+ * - Comfortable node types and helper functions for scalar arrays and tables.
+ * - Counter64, bit and truthvalue datatype support.
+ * - Callbacks for SNMP writes e.g. to implement persistency.
+ * - Runs on two APIs: RAW and netconn.
+ * - Async API is gone - the stack now supports netconn API instead,
+ * so blocking operations can be done in MIB calls.
+ * SNMP runs in a worker thread when netconn API is used.
+ * - Simplified thread sync support for MIBs - useful when MIBs
+ * need to access variables shared with other threads where no locking is
+ * possible. Used in MIB2 to access lwIP stats from lwIP thread.
+ *
+ * MIB compiler (code generator):
+ * ------------------------------
+ * - Provided in lwIP contrib repository.
+ * - Written in C#. MIB viewer used Windows Forms.
+ * - Developed on Windows with Visual Studio 2010.
+ * - Can be compiled and used on all platforms with http://www.monodevelop.com/.
+ * - Based on a heavily modified version of of SharpSnmpLib (a4bd05c6afb4)
+ * (https://sharpsnmplib.codeplex.com/SourceControl/network/forks/Nemo157/MIBParserUpdate).
+ * - MIB parser, C file generation framework and LWIP code generation are cleanly
+ * separated, which means the code may be useful as a base for code generation
+ * of other SNMP agents.
+ *
+ * Notes:
+ * ------
+ * - Stack and MIB compiler were used to implement a Profinet device.
+ * Compiled/implemented MIBs: LLDP-MIB, LLDP-EXT-DOT3-MIB, LLDP-EXT-PNO-MIB.
+ *
+ * SNMPv1 per RFC1157 and SNMPv2c per RFC 3416
+ * -------------------------------------------
+ * Note the S in SNMP stands for "Simple". Note that "Simple" is
+ * relative. SNMP is simple compared to the complex ISO network
+ * management protocols CMIP (Common Management Information Protocol)
+ * and CMOT (CMip Over Tcp).
+ *
+ * MIB II
+ * ------
+ * The standard lwIP stack management information base.
+ * This is a required MIB, so this is always enabled.
+ * The groups EGP, CMOT and transmission are disabled by default.
+ *
+ * Most mib-2 objects are not writable except:
+ * sysName, sysLocation, sysContact, snmpEnableAuthenTraps.
+ * Writing to or changing the ARP and IP address and route
+ * tables is not possible.
+ *
+ * Note lwIP has a very limited notion of IP routing. It currently
+ * doen't have a route table and doesn't have a notion of the U,G,H flags.
+ * Instead lwIP uses the interface list with only one default interface
+ * acting as a single gateway interface (G) for the default route.
+ *
+ * The agent returns a "virtual table" with the default route 0.0.0.0
+ * for the default interface and network routes (no H) for each
+ * network interface in the netif_list.
+ * All routes are considered to be up (U).
+ *
+ * Loading additional MIBs
+ * -----------------------
+ * MIBs can only be added in compile-time, not in run-time.
+ *
+ *
+ * 1 Building the Agent
+ * ====================
+ * First of all you'll need to add the following define
+ * to your local lwipopts.h:
+ * \#define LWIP_SNMP 1
+ *
+ * and add the source files your makefile.
+ *
+ * Note you'll might need to adapt you network driver to update
+ * the mib2 variables for your interface.
+ *
+ * 2 Running the Agent
+ * ===================
+ * The following function calls must be made in your program to
+ * actually get the SNMP agent running.
+ *
+ * Before starting the agent you should supply pointers
+ * for sysContact, sysLocation, and snmpEnableAuthenTraps.
+ * You can do this by calling
+ *
+ * - snmp_mib2_set_syscontact()
+ * - snmp_mib2_set_syslocation()
+ * - snmp_set_auth_traps_enabled()
+ *
+ * You can register a callback which is called on successful write access:
+ * snmp_set_write_callback().
+ *
+ * Additionally you may want to set
+ *
+ * - snmp_mib2_set_sysdescr()
+ * - snmp_set_device_enterprise_oid()
+ * - snmp_mib2_set_sysname()
+ *
+ * Also before starting the agent you need to setup
+ * one or more trap destinations using these calls:
+ *
+ * - snmp_trap_dst_enable()
+ * - snmp_trap_dst_ip_set()
+ *
+ * If you need more than MIB2, set the MIBs you want to use
+ * by snmp_set_mibs().
+ *
+ * Finally, enable the agent by calling snmp_init()
+ *
+ * @defgroup snmp_core Core
+ * @ingroup snmp
+ *
+ * @defgroup snmp_traps Traps
+ * @ingroup snmp
+ */
+
+#include "lwip/apps/snmp_opts.h"
+
+#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/apps/snmp.h"
+#include "lwip/apps/snmp_core.h"
+#include "snmp_core_priv.h"
+#include "lwip/netif.h"
+#include <string.h>
+
+
+#if (LWIP_SNMP && (SNMP_TRAP_DESTINATIONS<=0))
+ #error "If you want to use SNMP, you have to define SNMP_TRAP_DESTINATIONS>=1 in your lwipopts.h"
+#endif
+#if (!LWIP_UDP && LWIP_SNMP)
+ #error "If you want to use SNMP, you have to define LWIP_UDP=1 in your lwipopts.h"
+#endif
+
+struct snmp_statistics snmp_stats;
+static const struct snmp_obj_id snmp_device_enterprise_oid_default = {SNMP_DEVICE_ENTERPRISE_OID_LEN, SNMP_DEVICE_ENTERPRISE_OID};
+static const struct snmp_obj_id* snmp_device_enterprise_oid = &snmp_device_enterprise_oid_default;
+
+const u32_t snmp_zero_dot_zero_values[] = { 0, 0 };
+const struct snmp_obj_id_const_ref snmp_zero_dot_zero = { LWIP_ARRAYSIZE(snmp_zero_dot_zero_values), snmp_zero_dot_zero_values };
+
+
+#if SNMP_LWIP_MIB2
+#include "lwip/apps/snmp_mib2.h"
+static const struct snmp_mib* const default_mibs[] = { &mib2 };
+static u8_t snmp_num_mibs = 1;
+#else
+static const struct snmp_mib* const default_mibs[] = { NULL };
+static u8_t snmp_num_mibs = 0;
+#endif
+
+/* List of known mibs */
+static struct snmp_mib const * const *snmp_mibs = default_mibs;
+
+/**
+ * @ingroup snmp_core
+ * Sets the MIBs to use.
+ * Example: call snmp_set_mibs() as follows:
+ * static const struct snmp_mib *my_snmp_mibs[] = {
+ * &mib2,
+ * &private_mib
+ * };
+ * snmp_set_mibs(my_snmp_mibs, LWIP_ARRAYSIZE(my_snmp_mibs));
+ */
+void
+snmp_set_mibs(const struct snmp_mib **mibs, u8_t num_mibs)
+{
+ LWIP_ASSERT("mibs pointer must be != NULL", (mibs != NULL));
+ LWIP_ASSERT("num_mibs pointer must be != 0", (num_mibs != 0));
+ snmp_mibs = mibs;
+ snmp_num_mibs = num_mibs;
+}
+
+/**
+ * @ingroup snmp_core
+ * 'device enterprise oid' is used for 'device OID' field in trap PDU's (for identification of generating device)
+ * as well as for value returned by MIB-2 'sysObjectID' field (if internal MIB2 implementation is used).
+ * The 'device enterprise oid' shall point to an OID located under 'private-enterprises' branch (1.3.6.1.4.1.XXX). If a vendor
+ * wants to provide a custom object there, he has to get its own enterprise oid from IANA (http://www.iana.org). It
+ * is not allowed to use LWIP enterprise ID!
+ * In order to identify a specific device it is recommended to create a dedicated OID for each device type under its own
+ * enterprise oid.
+ * e.g.
+ * device a > 1.3.6.1.4.1.XXX(ent-oid).1(devices).1(device a)
+ * device b > 1.3.6.1.4.1.XXX(ent-oid).1(devices).2(device b)
+ * for more details see description of 'sysObjectID' field in RFC1213-MIB
+ */
+void snmp_set_device_enterprise_oid(const struct snmp_obj_id* device_enterprise_oid)
+{
+ if (device_enterprise_oid == NULL) {
+ snmp_device_enterprise_oid = &snmp_device_enterprise_oid_default;
+ } else {
+ snmp_device_enterprise_oid = device_enterprise_oid;
+ }
+}
+
+/**
+ * @ingroup snmp_core
+ * Get 'device enterprise oid'
+ */
+const struct snmp_obj_id* snmp_get_device_enterprise_oid(void)
+{
+ return snmp_device_enterprise_oid;
+}
+
+#if LWIP_IPV4
+/**
+ * Conversion from InetAddressIPv4 oid to lwIP ip4_addr
+ * @param oid points to u32_t ident[4] input
+ * @param ip points to output struct
+ */
+u8_t
+snmp_oid_to_ip4(const u32_t *oid, ip4_addr_t *ip)
+{
+ if ((oid[0] > 0xFF) ||
+ (oid[1] > 0xFF) ||
+ (oid[2] > 0xFF) ||
+ (oid[3] > 0xFF)) {
+ ip4_addr_copy(*ip, *IP4_ADDR_ANY4);
+ return 0;
+ }
+
+ IP4_ADDR(ip, oid[0], oid[1], oid[2], oid[3]);
+ return 1;
+}
+
+/**
+ * Convert ip4_addr to InetAddressIPv4 (no InetAddressType)
+ * @param ip points to input struct
+ * @param oid points to u32_t ident[4] output
+ */
+void
+snmp_ip4_to_oid(const ip4_addr_t *ip, u32_t *oid)
+{
+ oid[0] = ip4_addr1(ip);
+ oid[1] = ip4_addr2(ip);
+ oid[2] = ip4_addr3(ip);
+ oid[3] = ip4_addr4(ip);
+}
+#endif /* LWIP_IPV4 */
+
+#if LWIP_IPV6
+/**
+ * Conversion from InetAddressIPv6 oid to lwIP ip6_addr
+ * @param oid points to u32_t oid[16] input
+ * @param ip points to output struct
+ */
+u8_t
+snmp_oid_to_ip6(const u32_t *oid, ip6_addr_t *ip)
+{
+ if ((oid[0] > 0xFF) ||
+ (oid[1] > 0xFF) ||
+ (oid[2] > 0xFF) ||
+ (oid[3] > 0xFF) ||
+ (oid[4] > 0xFF) ||
+ (oid[5] > 0xFF) ||
+ (oid[6] > 0xFF) ||
+ (oid[7] > 0xFF) ||
+ (oid[8] > 0xFF) ||
+ (oid[9] > 0xFF) ||
+ (oid[10] > 0xFF) ||
+ (oid[11] > 0xFF) ||
+ (oid[12] > 0xFF) ||
+ (oid[13] > 0xFF) ||
+ (oid[14] > 0xFF) ||
+ (oid[15] > 0xFF)) {
+ ip6_addr_set_any(ip);
+ return 0;
+ }
+
+ ip->addr[0] = (oid[0] << 24) | (oid[1] << 16) | (oid[2] << 8) | (oid[3] << 0);
+ ip->addr[1] = (oid[4] << 24) | (oid[5] << 16) | (oid[6] << 8) | (oid[7] << 0);
+ ip->addr[2] = (oid[8] << 24) | (oid[9] << 16) | (oid[10] << 8) | (oid[11] << 0);
+ ip->addr[3] = (oid[12] << 24) | (oid[13] << 16) | (oid[14] << 8) | (oid[15] << 0);
+ return 1;
+}
+
+/**
+ * Convert ip6_addr to InetAddressIPv6 (no InetAddressType)
+ * @param ip points to input struct
+ * @param oid points to u32_t ident[16] output
+ */
+void
+snmp_ip6_to_oid(const ip6_addr_t *ip, u32_t *oid)
+{
+ oid[0] = (ip->addr[0] & 0xFF000000) >> 24;
+ oid[1] = (ip->addr[0] & 0x00FF0000) >> 16;
+ oid[2] = (ip->addr[0] & 0x0000FF00) >> 8;
+ oid[3] = (ip->addr[0] & 0x000000FF) >> 0;
+ oid[4] = (ip->addr[1] & 0xFF000000) >> 24;
+ oid[5] = (ip->addr[1] & 0x00FF0000) >> 16;
+ oid[6] = (ip->addr[1] & 0x0000FF00) >> 8;
+ oid[7] = (ip->addr[1] & 0x000000FF) >> 0;
+ oid[8] = (ip->addr[2] & 0xFF000000) >> 24;
+ oid[9] = (ip->addr[2] & 0x00FF0000) >> 16;
+ oid[10] = (ip->addr[2] & 0x0000FF00) >> 8;
+ oid[11] = (ip->addr[2] & 0x000000FF) >> 0;
+ oid[12] = (ip->addr[3] & 0xFF000000) >> 24;
+ oid[13] = (ip->addr[3] & 0x00FF0000) >> 16;
+ oid[14] = (ip->addr[3] & 0x0000FF00) >> 8;
+ oid[15] = (ip->addr[3] & 0x000000FF) >> 0;
+}
+#endif /* LWIP_IPV6 */
+
+#if LWIP_IPV4 || LWIP_IPV6
+/**
+ * Convert to InetAddressType+InetAddress+InetPortNumber
+ * @param ip IP address
+ * @param port Port
+ * @param oid OID
+ * @return OID length
+ */
+u8_t
+snmp_ip_port_to_oid(const ip_addr_t *ip, u16_t port, u32_t *oid)
+{
+ u8_t idx;
+
+ idx = snmp_ip_to_oid(ip, oid);
+ oid[idx] = port;
+ idx++;
+
+ return idx;
+}
+
+/**
+ * Convert to InetAddressType+InetAddress
+ * @param ip IP address
+ * @param oid OID
+ * @return OID length
+ */
+u8_t
+snmp_ip_to_oid(const ip_addr_t *ip, u32_t *oid)
+{
+ if (IP_IS_ANY_TYPE_VAL(*ip)) {
+ oid[0] = 0; /* any */
+ oid[1] = 0; /* no IP OIDs follow */
+ return 2;
+ } else if (IP_IS_V6(ip)) {
+#if LWIP_IPV6
+ oid[0] = 2; /* ipv6 */
+ oid[1] = 16; /* 16 InetAddressIPv6 OIDs follow */
+ snmp_ip6_to_oid(ip_2_ip6(ip), &oid[2]);
+ return 18;
+#else /* LWIP_IPV6 */
+ return 0;
+#endif /* LWIP_IPV6 */
+ } else {
+#if LWIP_IPV4
+ oid[0] = 1; /* ipv4 */
+ oid[1] = 4; /* 4 InetAddressIPv4 OIDs follow */
+ snmp_ip4_to_oid(ip_2_ip4(ip), &oid[2]);
+ return 6;
+#else /* LWIP_IPV4 */
+ return 0;
+#endif /* LWIP_IPV4 */
+ }
+}
+
+/**
+ * Convert from InetAddressType+InetAddress to ip_addr_t
+ * @param oid OID
+ * @param oid_len OID length
+ * @param ip IP address
+ * @return Parsed OID length
+ */
+u8_t
+snmp_oid_to_ip(const u32_t *oid, u8_t oid_len, ip_addr_t *ip)
+{
+ /* InetAddressType */
+ if (oid_len < 1) {
+ return 0;
+ }
+
+ if (oid[0] == 0) { /* any */
+ /* 1x InetAddressType, 1x OID len */
+ if (oid_len < 2) {
+ return 0;
+ }
+ if (oid[1] != 0) {
+ return 0;
+ }
+
+ memset(ip, 0, sizeof(*ip));
+ IP_SET_TYPE(ip, IPADDR_TYPE_ANY);
+
+ return 2;
+ } else if (oid[0] == 1) { /* ipv4 */
+#if LWIP_IPV4
+ /* 1x InetAddressType, 1x OID len, 4x InetAddressIPv4 */
+ if (oid_len < 6) {
+ return 0;
+ }
+
+ /* 4x ipv4 OID */
+ if (oid[1] != 4) {
+ return 0;
+ }
+
+ IP_SET_TYPE(ip, IPADDR_TYPE_V4);
+ if (!snmp_oid_to_ip4(&oid[2], ip_2_ip4(ip))) {
+ return 0;
+ }
+
+ return 6;
+#else /* LWIP_IPV4 */
+ return 0;
+#endif /* LWIP_IPV4 */
+ } else if (oid[0] == 2) { /* ipv6 */
+#if LWIP_IPV6
+ /* 1x InetAddressType, 1x OID len, 16x InetAddressIPv6 */
+ if (oid_len < 18) {
+ return 0;
+ }
+
+ /* 16x ipv6 OID */
+ if (oid[1] != 16) {
+ return 0;
+ }
+
+ IP_SET_TYPE(ip, IPADDR_TYPE_V6);
+ if (!snmp_oid_to_ip6(&oid[2], ip_2_ip6(ip))) {
+ return 0;
+ }
+
+ return 18;
+#else /* LWIP_IPV6 */
+ return 0;
+#endif /* LWIP_IPV6 */
+ } else { /* unsupported InetAddressType */
+ return 0;
+ }
+}
+
+/**
+ * Convert from InetAddressType+InetAddress+InetPortNumber to ip_addr_t and u16_t
+ * @param oid OID
+ * @param oid_len OID length
+ * @param ip IP address
+ * @param port Port
+ * @return Parsed OID length
+ */
+u8_t
+snmp_oid_to_ip_port(const u32_t *oid, u8_t oid_len, ip_addr_t *ip, u16_t *port)
+{
+ u8_t idx = 0;
+
+ /* InetAddressType + InetAddress */
+ idx += snmp_oid_to_ip(&oid[idx], oid_len-idx, ip);
+ if (idx == 0) {
+ return 0;
+ }
+
+ /* InetPortNumber */
+ if (oid_len < (idx+1)) {
+ return 0;
+ }
+ if (oid[idx] > 0xffff) {
+ return 0;
+ }
+ *port = (u16_t)oid[idx];
+ idx++;
+
+ return idx;
+}
+
+#endif /* LWIP_IPV4 || LWIP_IPV6 */
+
+/**
+ * Assign an OID to struct snmp_obj_id
+ * @param target Assignment target
+ * @param oid OID
+ * @param oid_len OID length
+ */
+void
+snmp_oid_assign(struct snmp_obj_id* target, const u32_t *oid, u8_t oid_len)
+{
+ LWIP_ASSERT("oid_len <= LWIP_SNMP_OBJ_ID_LEN", oid_len <= SNMP_MAX_OBJ_ID_LEN);
+
+ target->len = oid_len;
+
+ if (oid_len > 0) {
+ MEMCPY(target->id, oid, oid_len * sizeof(u32_t));
+ }
+}
+
+/**
+ * Prefix an OID to OID in struct snmp_obj_id
+ * @param target Assignment target to prefix
+ * @param oid OID
+ * @param oid_len OID length
+ */
+void
+snmp_oid_prefix(struct snmp_obj_id* target, const u32_t *oid, u8_t oid_len)
+{
+ LWIP_ASSERT("target->len + oid_len <= LWIP_SNMP_OBJ_ID_LEN", (target->len + oid_len) <= SNMP_MAX_OBJ_ID_LEN);
+
+ if (oid_len > 0) {
+ /* move existing OID to make room at the beginning for OID to insert */
+ int i;
+ for (i = target->len-1; i>=0; i--) {
+ target->id[i + oid_len] = target->id[i];
+ }
+
+ /* paste oid at the beginning */
+ MEMCPY(target->id, oid, oid_len * sizeof(u32_t));
+ }
+}
+
+/**
+ * Combine two OIDs into struct snmp_obj_id
+ * @param target Assignmet target
+ * @param oid1 OID 1
+ * @param oid1_len OID 1 length
+ * @param oid2 OID 2
+ * @param oid2_len OID 2 length
+ */
+void
+snmp_oid_combine(struct snmp_obj_id* target, const u32_t *oid1, u8_t oid1_len, const u32_t *oid2, u8_t oid2_len)
+{
+ snmp_oid_assign(target, oid1, oid1_len);
+ snmp_oid_append(target, oid2, oid2_len);
+}
+
+/**
+ * Append OIDs to struct snmp_obj_id
+ * @param target Assignment target to append to
+ * @param oid OID
+ * @param oid_len OID length
+ */
+void
+snmp_oid_append(struct snmp_obj_id* target, const u32_t *oid, u8_t oid_len)
+{
+ LWIP_ASSERT("offset + oid_len <= LWIP_SNMP_OBJ_ID_LEN", (target->len + oid_len) <= SNMP_MAX_OBJ_ID_LEN);
+
+ if (oid_len > 0) {
+ MEMCPY(&target->id[target->len], oid, oid_len * sizeof(u32_t));
+ target->len += oid_len;
+ }
+}
+
+/**
+ * Compare two OIDs
+ * @param oid1 OID 1
+ * @param oid1_len OID 1 length
+ * @param oid2 OID 2
+ * @param oid2_len OID 2 length
+ * @return -1: OID1&lt;OID2 1: OID1 &gt;OID2 0: equal
+ */
+s8_t
+snmp_oid_compare(const u32_t *oid1, u8_t oid1_len, const u32_t *oid2, u8_t oid2_len)
+{
+ u8_t level = 0;
+ LWIP_ASSERT("'oid1' param must not be NULL or 'oid1_len' param be 0!", (oid1 != NULL) || (oid1_len == 0));
+ LWIP_ASSERT("'oid2' param must not be NULL or 'oid2_len' param be 0!", (oid2 != NULL) || (oid2_len == 0));
+
+ while ((level < oid1_len) && (level < oid2_len)) {
+ if (*oid1 < *oid2) {
+ return -1;
+ }
+ if (*oid1 > *oid2) {
+ return 1;
+ }
+
+ level++;
+ oid1++;
+ oid2++;
+ }
+
+ /* common part of both OID's is equal, compare length */
+ if (oid1_len < oid2_len) {
+ return -1;
+ }
+ if (oid1_len > oid2_len) {
+ return 1;
+ }
+
+ /* they are equal */
+ return 0;
+}
+
+
+/**
+ * Check of two OIDs are equal
+ * @param oid1 OID 1
+ * @param oid1_len OID 1 length
+ * @param oid2 OID 2
+ * @param oid2_len OID 2 length
+ * @return 1: equal 0: non-equal
+ */
+u8_t
+snmp_oid_equal(const u32_t *oid1, u8_t oid1_len, const u32_t *oid2, u8_t oid2_len)
+{
+ return (snmp_oid_compare(oid1, oid1_len, oid2, oid2_len) == 0)? 1 : 0;
+}
+
+/**
+ * Convert netif to interface index
+ * @param netif netif
+ * @return index
+ */
+u8_t
+netif_to_num(const struct netif *netif)
+{
+ u8_t result = 0;
+ struct netif *netif_iterator = netif_list;
+
+ while (netif_iterator != NULL) {
+ result++;
+
+ if (netif_iterator == netif) {
+ return result;
+ }
+
+ netif_iterator = netif_iterator->next;
+ }
+
+ LWIP_ASSERT("netif not found in netif_list", 0);
+ return 0;
+}
+
+static const struct snmp_mib*
+snmp_get_mib_from_oid(const u32_t *oid, u8_t oid_len)
+{
+ const u32_t* list_oid;
+ const u32_t* searched_oid;
+ u8_t i, l;
+
+ u8_t max_match_len = 0;
+ const struct snmp_mib* matched_mib = NULL;
+
+ LWIP_ASSERT("'oid' param must not be NULL!", (oid != NULL));
+
+ if (oid_len == 0) {
+ return NULL;
+ }
+
+ for (i = 0; i < snmp_num_mibs; i++) {
+ LWIP_ASSERT("MIB array not initialized correctly", (snmp_mibs[i] != NULL));
+ LWIP_ASSERT("MIB array not initialized correctly - base OID is NULL", (snmp_mibs[i]->base_oid != NULL));
+
+ if (oid_len >= snmp_mibs[i]->base_oid_len) {
+ l = snmp_mibs[i]->base_oid_len;
+ list_oid = snmp_mibs[i]->base_oid;
+ searched_oid = oid;
+
+ while (l > 0) {
+ if (*list_oid != *searched_oid) {
+ break;
+ }
+
+ l--;
+ list_oid++;
+ searched_oid++;
+ }
+
+ if ((l == 0) && (snmp_mibs[i]->base_oid_len > max_match_len)) {
+ max_match_len = snmp_mibs[i]->base_oid_len;
+ matched_mib = snmp_mibs[i];
+ }
+ }
+ }
+
+ return matched_mib;
+}
+
+static const struct snmp_mib*
+snmp_get_next_mib(const u32_t *oid, u8_t oid_len)
+{
+ u8_t i;
+ const struct snmp_mib* next_mib = NULL;
+
+ LWIP_ASSERT("'oid' param must not be NULL!", (oid != NULL));
+
+ if (oid_len == 0) {
+ return NULL;
+ }
+
+ for (i = 0; i < snmp_num_mibs; i++) {
+ if (snmp_mibs[i]->base_oid != NULL) {
+ /* check if mib is located behind starting point */
+ if (snmp_oid_compare(snmp_mibs[i]->base_oid, snmp_mibs[i]->base_oid_len, oid, oid_len) > 0) {
+ if ((next_mib == NULL) ||
+ (snmp_oid_compare(snmp_mibs[i]->base_oid, snmp_mibs[i]->base_oid_len,
+ next_mib->base_oid, next_mib->base_oid_len) < 0)) {
+ next_mib = snmp_mibs[i];
+ }
+ }
+ }
+ }
+
+ return next_mib;
+}
+
+static const struct snmp_mib*
+snmp_get_mib_between(const u32_t *oid1, u8_t oid1_len, const u32_t *oid2, u8_t oid2_len)
+{
+ const struct snmp_mib* next_mib = snmp_get_next_mib(oid1, oid1_len);
+
+ LWIP_ASSERT("'oid2' param must not be NULL!", (oid2 != NULL));
+ LWIP_ASSERT("'oid2_len' param must be greater than 0!", (oid2_len > 0));
+
+ if (next_mib != NULL) {
+ if (snmp_oid_compare(next_mib->base_oid, next_mib->base_oid_len, oid2, oid2_len) < 0) {
+ return next_mib;
+ }
+ }
+
+ return NULL;
+}
+
+u8_t
+snmp_get_node_instance_from_oid(const u32_t *oid, u8_t oid_len, struct snmp_node_instance* node_instance)
+{
+ u8_t result = SNMP_ERR_NOSUCHOBJECT;
+ const struct snmp_mib *mib;
+ const struct snmp_node *mn = NULL;
+
+ mib = snmp_get_mib_from_oid(oid, oid_len);
+ if (mib != NULL) {
+ u8_t oid_instance_len;
+
+ mn = snmp_mib_tree_resolve_exact(mib, oid, oid_len, &oid_instance_len);
+ if ((mn != NULL) && (mn->node_type != SNMP_NODE_TREE)) {
+ /* get instance */
+ const struct snmp_leaf_node* leaf_node = (const struct snmp_leaf_node*)(const void*)mn;
+
+ node_instance->node = mn;
+ snmp_oid_assign(&node_instance->instance_oid, oid + (oid_len - oid_instance_len), oid_instance_len);
+
+ result = leaf_node->get_instance(
+ oid,
+ oid_len - oid_instance_len,
+ node_instance);
+
+#ifdef LWIP_DEBUG
+ if (result == SNMP_ERR_NOERROR) {
+ if (((node_instance->access & SNMP_NODE_INSTANCE_ACCESS_READ) != 0) && (node_instance->get_value == NULL)) {
+ LWIP_DEBUGF(SNMP_DEBUG, ("SNMP inconsistent access: node is readable but no get_value function is specified\n"));
+ }
+ if (((node_instance->access & SNMP_NODE_INSTANCE_ACCESS_WRITE) != 0) && (node_instance->set_value == NULL)) {
+ LWIP_DEBUGF(SNMP_DEBUG, ("SNMP inconsistent access: node is writable but no set_value and/or set_test function is specified\n"));
+ }
+ }
+#endif
+ }
+ }
+
+ return result;
+}
+
+u8_t
+snmp_get_next_node_instance_from_oid(const u32_t *oid, u8_t oid_len, snmp_validate_node_instance_method validate_node_instance_method, void* validate_node_instance_arg, struct snmp_obj_id* node_oid, struct snmp_node_instance* node_instance)
+{
+ const struct snmp_mib *mib;
+ const struct snmp_node *mn = NULL;
+ const u32_t* start_oid = NULL;
+ u8_t start_oid_len = 0;
+
+ /* resolve target MIB from passed OID */
+ mib = snmp_get_mib_from_oid(oid, oid_len);
+ if (mib == NULL) {
+ /* passed OID does not reference any known MIB, start at the next closest MIB */
+ mib = snmp_get_next_mib(oid, oid_len);
+
+ if (mib != NULL) {
+ start_oid = mib->base_oid;
+ start_oid_len = mib->base_oid_len;
+ }
+ } else {
+ start_oid = oid;
+ start_oid_len = oid_len;
+ }
+
+ /* resolve target node from MIB, skip to next MIB if no suitable node is found in current MIB */
+ while ((mib != NULL) && (mn == NULL)) {
+ u8_t oid_instance_len;
+
+ /* check if OID directly references a node inside current MIB, in this case we have to ask this node for the next instance */
+ mn = snmp_mib_tree_resolve_exact(mib, start_oid, start_oid_len, &oid_instance_len);
+ if (mn != NULL) {
+ snmp_oid_assign(node_oid, start_oid, start_oid_len - oid_instance_len); /* set oid to node */
+ snmp_oid_assign(&node_instance->instance_oid, start_oid + (start_oid_len - oid_instance_len), oid_instance_len); /* set (relative) instance oid */
+ } else {
+ /* OID does not reference a node, search for the next closest node inside MIB; set instance_oid.len to zero because we want the first instance of this node */
+ mn = snmp_mib_tree_resolve_next(mib, start_oid, start_oid_len, node_oid);
+ node_instance->instance_oid.len = 0;
+ }
+
+ /* validate the node; if the node has no further instance or the returned instance is invalid, search for the next in MIB and validate again */
+ node_instance->node = mn;
+ while (mn != NULL) {
+ u8_t result;
+
+ /* clear fields which may have values from previous loops */
+ node_instance->asn1_type = 0;
+ node_instance->access = SNMP_NODE_INSTANCE_NOT_ACCESSIBLE;
+ node_instance->get_value = NULL;
+ node_instance->set_test = NULL;
+ node_instance->set_value = NULL;
+ node_instance->release_instance = NULL;
+ node_instance->reference.ptr = NULL;
+ node_instance->reference_len = 0;
+
+ result = ((const struct snmp_leaf_node*)(const void*)mn)->get_next_instance(
+ node_oid->id,
+ node_oid->len,
+ node_instance);
+
+ if (result == SNMP_ERR_NOERROR) {
+#ifdef LWIP_DEBUG
+ if (((node_instance->access & SNMP_NODE_INSTANCE_ACCESS_READ) != 0) && (node_instance->get_value == NULL)) {
+ LWIP_DEBUGF(SNMP_DEBUG, ("SNMP inconsistent access: node is readable but no get_value function is specified\n"));
+ }
+ if (((node_instance->access & SNMP_NODE_INSTANCE_ACCESS_WRITE) != 0) && (node_instance->set_value == NULL)) {
+ LWIP_DEBUGF(SNMP_DEBUG, ("SNMP inconsistent access: node is writable but no set_value function is specified\n"));
+ }
+#endif
+
+ /* validate node because the node may be not accessible for example (but let the caller decide what is valid */
+ if ((validate_node_instance_method == NULL) ||
+ (validate_node_instance_method(node_instance, validate_node_instance_arg) == SNMP_ERR_NOERROR)) {
+ /* node_oid "returns" the full result OID (including the instance part) */
+ snmp_oid_append(node_oid, node_instance->instance_oid.id, node_instance->instance_oid.len);
+ break;
+ }
+
+ if (node_instance->release_instance != NULL) {
+ node_instance->release_instance(node_instance);
+ }
+ /*
+ the instance itself is not valid, ask for next instance from same node.
+ we don't have to change any variables because node_instance->instance_oid is used as input (starting point)
+ as well as output (resulting next OID), so we have to simply call get_next_instance method again
+ */
+ } else {
+ if (node_instance->release_instance != NULL) {
+ node_instance->release_instance(node_instance);
+ }
+
+ /* the node has no further instance, skip to next node */
+ mn = snmp_mib_tree_resolve_next(mib, node_oid->id, node_oid->len, &node_instance->instance_oid); /* misuse node_instance->instance_oid as tmp buffer */
+ if (mn != NULL) {
+ /* prepare for next loop */
+ snmp_oid_assign(node_oid, node_instance->instance_oid.id, node_instance->instance_oid.len);
+ node_instance->instance_oid.len = 0;
+ node_instance->node = mn;
+ }
+ }
+ }
+
+ if (mn != NULL) {
+ /*
+ we found a suitable next node,
+ now we have to check if a inner MIB is located between the searched OID and the resulting OID.
+ this is possible because MIB's may be located anywhere in the global tree, that means also in
+ the subtree of another MIB (e.g. if searched OID is .2 and resulting OID is .4, then another
+ MIB having .3 as root node may exist)
+ */
+ const struct snmp_mib *intermediate_mib;
+ intermediate_mib = snmp_get_mib_between(start_oid, start_oid_len, node_oid->id, node_oid->len);
+
+ if (intermediate_mib != NULL) {
+ /* search for first node inside intermediate mib in next loop */
+ if (node_instance->release_instance != NULL) {
+ node_instance->release_instance(node_instance);
+ }
+
+ mn = NULL;
+ mib = intermediate_mib;
+ start_oid = mib->base_oid;
+ start_oid_len = mib->base_oid_len;
+ }
+ /* else { we found out target node } */
+ } else {
+ /*
+ there is no further (suitable) node inside this MIB, search for the next MIB with following priority
+ 1. search for inner MIB's (whose root is located inside tree of current MIB)
+ 2. search for surrouding MIB's (where the current MIB is the inner MIB) and continue there if any
+ 3. take the next closest MIB (not being related to the current MIB)
+ */
+ const struct snmp_mib *next_mib;
+ next_mib = snmp_get_next_mib(start_oid, start_oid_len); /* returns MIB's related to point 1 and 3 */
+
+ /* is the found MIB an inner MIB? (point 1) */
+ if ((next_mib != NULL) && (next_mib->base_oid_len > mib->base_oid_len) &&
+ (snmp_oid_compare(next_mib->base_oid, mib->base_oid_len, mib->base_oid, mib->base_oid_len) == 0)) {
+ /* yes it is -> continue at inner MIB */
+ mib = next_mib;
+ start_oid = mib->base_oid;
+ start_oid_len = mib->base_oid_len;
+ } else {
+ /* check if there is a surrounding mib where to continue (point 2) (only possible if OID length > 1) */
+ if (mib->base_oid_len > 1) {
+ mib = snmp_get_mib_from_oid(mib->base_oid, mib->base_oid_len - 1);
+
+ if (mib == NULL) {
+ /* no surrounding mib, use next mib encountered above (point 3) */
+ mib = next_mib;
+
+ if (mib != NULL) {
+ start_oid = mib->base_oid;
+ start_oid_len = mib->base_oid_len;
+ }
+ }
+ /* else { start_oid stays the same because we want to continue from current offset in surrounding mib (point 2) } */
+ }
+ }
+ }
+ }
+
+ if (mib == NULL) {
+ /* loop is only left when mib == null (error) or mib_node != NULL (success) */
+ return SNMP_ERR_ENDOFMIBVIEW;
+ }
+
+ return SNMP_ERR_NOERROR;
+}
+
+/**
+ * Searches tree for the supplied object identifier.
+ *
+ */
+const struct snmp_node *
+snmp_mib_tree_resolve_exact(const struct snmp_mib *mib, const u32_t *oid, u8_t oid_len, u8_t* oid_instance_len)
+{
+ const struct snmp_node* const* node = &mib->root_node;
+ u8_t oid_offset = mib->base_oid_len;
+
+ while ((oid_offset < oid_len) && ((*node)->node_type == SNMP_NODE_TREE)) {
+ /* search for matching sub node */
+ u32_t subnode_oid = *(oid + oid_offset);
+
+ u32_t i = (*(const struct snmp_tree_node* const*)node)->subnode_count;
+ node = (*(const struct snmp_tree_node* const*)node)->subnodes;
+ while ((i > 0) && ((*node)->oid != subnode_oid)) {
+ node++;
+ i--;
+ }
+
+ if (i == 0) {
+ /* no matching subnode found */
+ return NULL;
+ }
+
+ oid_offset++;
+ }
+
+ if ((*node)->node_type != SNMP_NODE_TREE) {
+ /* we found a leaf node */
+ *oid_instance_len = oid_len - oid_offset;
+ return (*node);
+ }
+
+ return NULL;
+}
+
+const struct snmp_node*
+snmp_mib_tree_resolve_next(const struct snmp_mib *mib, const u32_t *oid, u8_t oid_len, struct snmp_obj_id* oidret)
+{
+ u8_t oid_offset = mib->base_oid_len;
+ const struct snmp_node* const* node;
+ const struct snmp_tree_node* node_stack[SNMP_MAX_OBJ_ID_LEN];
+ s32_t nsi = 0; /* NodeStackIndex */
+ u32_t subnode_oid;
+
+ if (mib->root_node->node_type != SNMP_NODE_TREE) {
+ /* a next operation on a mib with only a leaf node will always return NULL because there is no other node */
+ return NULL;
+ }
+
+ /* first build node stack related to passed oid (as far as possible), then go backwards to determine the next node */
+ node_stack[nsi] = (const struct snmp_tree_node*)(const void*)mib->root_node;
+ while (oid_offset < oid_len) {
+ /* search for matching sub node */
+ u32_t i = node_stack[nsi]->subnode_count;
+ node = node_stack[nsi]->subnodes;
+
+ subnode_oid = *(oid + oid_offset);
+
+ while ((i > 0) && ((*node)->oid != subnode_oid)) {
+ node++;
+ i--;
+ }
+
+ if ((i == 0) || ((*node)->node_type != SNMP_NODE_TREE)) {
+ /* no (matching) tree-subnode found */
+ break;
+ }
+ nsi++;
+ node_stack[nsi] = (const struct snmp_tree_node*)(const void*)(*node);
+
+ oid_offset++;
+ }
+
+
+ if (oid_offset >= oid_len) {
+ /* passed oid references a tree node -> return first useable sub node of it */
+ subnode_oid = 0;
+ } else {
+ subnode_oid = *(oid + oid_offset) + 1;
+ }
+
+ while (nsi >= 0) {
+ const struct snmp_node* subnode = NULL;
+
+ /* find next node on current level */
+ s32_t i = node_stack[nsi]->subnode_count;
+ node = node_stack[nsi]->subnodes;
+ while (i > 0) {
+ if ((*node)->oid == subnode_oid) {
+ subnode = *node;
+ break;
+ } else if (((*node)->oid > subnode_oid) && ((subnode == NULL) || ((*node)->oid < subnode->oid))) {
+ subnode = *node;
+ }
+
+ node++;
+ i--;
+ }
+
+ if (subnode == NULL) {
+ /* no further node found on this level, go one level up and start searching with index of current node*/
+ subnode_oid = node_stack[nsi]->node.oid + 1;
+ nsi--;
+ } else {
+ if (subnode->node_type == SNMP_NODE_TREE) {
+ /* next is a tree node, go into it and start searching */
+ nsi++;
+ node_stack[nsi] = (const struct snmp_tree_node*)(const void*)subnode;
+ subnode_oid = 0;
+ } else {
+ /* we found a leaf node -> fill oidret and return it */
+ snmp_oid_assign(oidret, mib->base_oid, mib->base_oid_len);
+ i = 1;
+ while (i <= nsi) {
+ oidret->id[oidret->len] = node_stack[i]->node.oid;
+ oidret->len++;
+ i++;
+ }
+
+ oidret->id[oidret->len] = subnode->oid;
+ oidret->len++;
+
+ return subnode;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+/** initialize struct next_oid_state using this function before passing it to next_oid_check */
+void
+snmp_next_oid_init(struct snmp_next_oid_state *state,
+ const u32_t *start_oid, u8_t start_oid_len,
+ u32_t *next_oid_buf, u8_t next_oid_max_len)
+{
+ state->start_oid = start_oid;
+ state->start_oid_len = start_oid_len;
+ state->next_oid = next_oid_buf;
+ state->next_oid_len = 0;
+ state->next_oid_max_len = next_oid_max_len;
+ state->status = SNMP_NEXT_OID_STATUS_NO_MATCH;
+}
+
+/** checks if the passed incomplete OID may be a possible candidate for snmp_next_oid_check();
+this methid is intended if the complete OID is not yet known but it is very expensive to build it up,
+so it is possible to test the starting part before building up the complete oid and pass it to snmp_next_oid_check()*/
+u8_t
+snmp_next_oid_precheck(struct snmp_next_oid_state *state, const u32_t *oid, const u8_t oid_len)
+{
+ if (state->status != SNMP_NEXT_OID_STATUS_BUF_TO_SMALL) {
+ u8_t start_oid_len = (oid_len < state->start_oid_len) ? oid_len : state->start_oid_len;
+
+ /* check passed OID is located behind start offset */
+ if (snmp_oid_compare(oid, oid_len, state->start_oid, start_oid_len) >= 0) {
+ /* check if new oid is located closer to start oid than current closest oid */
+ if ((state->status == SNMP_NEXT_OID_STATUS_NO_MATCH) ||
+ (snmp_oid_compare(oid, oid_len, state->next_oid, state->next_oid_len) < 0)) {
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/** checks the passed OID if it is a candidate to be the next one (get_next); returns !=0 if passed oid is currently closest, otherwise 0 */
+u8_t
+snmp_next_oid_check(struct snmp_next_oid_state *state, const u32_t *oid, const u8_t oid_len, void* reference)
+{
+ /* do not overwrite a fail result */
+ if (state->status != SNMP_NEXT_OID_STATUS_BUF_TO_SMALL) {
+ /* check passed OID is located behind start offset */
+ if (snmp_oid_compare(oid, oid_len, state->start_oid, state->start_oid_len) > 0) {
+ /* check if new oid is located closer to start oid than current closest oid */
+ if ((state->status == SNMP_NEXT_OID_STATUS_NO_MATCH) ||
+ (snmp_oid_compare(oid, oid_len, state->next_oid, state->next_oid_len) < 0)) {
+ if (oid_len <= state->next_oid_max_len) {
+ MEMCPY(state->next_oid, oid, oid_len * sizeof(u32_t));
+ state->next_oid_len = oid_len;
+ state->status = SNMP_NEXT_OID_STATUS_SUCCESS;
+ state->reference = reference;
+ return 1;
+ } else {
+ state->status = SNMP_NEXT_OID_STATUS_BUF_TO_SMALL;
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+u8_t
+snmp_oid_in_range(const u32_t *oid_in, u8_t oid_len, const struct snmp_oid_range *oid_ranges, u8_t oid_ranges_len)
+{
+ u8_t i;
+
+ if (oid_len != oid_ranges_len) {
+ return 0;
+ }
+
+ for (i = 0; i < oid_ranges_len; i++) {
+ if ((oid_in[i] < oid_ranges[i].min) || (oid_in[i] > oid_ranges[i].max)) {
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+snmp_err_t
+snmp_set_test_ok(struct snmp_node_instance* instance, u16_t value_len, void* value)
+{
+ LWIP_UNUSED_ARG(instance);
+ LWIP_UNUSED_ARG(value_len);
+ LWIP_UNUSED_ARG(value);
+
+ return SNMP_ERR_NOERROR;
+}
+
+/**
+ * Decodes BITS pseudotype value from ASN.1 OctetString.
+ *
+ * @note Because BITS pseudo type is encoded as OCTET STRING, it cannot directly
+ * be encoded/decoded by the agent. Instead call this function as required from
+ * get/test/set methods.
+ *
+ * @param buf points to a buffer holding the ASN1 octet string
+ * @param buf_len length of octet string
+ * @param bit_value decoded Bit value with Bit0 == LSB
+ * @return ERR_OK if successful, ERR_ARG if bit value contains more than 32 bit
+ */
+err_t
+snmp_decode_bits(const u8_t *buf, u32_t buf_len, u32_t *bit_value)
+{
+ u8_t b;
+ u8_t bits_processed = 0;
+ *bit_value = 0;
+
+ while (buf_len > 0) {
+ /* any bit set in this byte? */
+ if (*buf != 0x00) {
+ if (bits_processed >= 32) {
+ /* accept more than 4 bytes, but only when no bits are set */
+ return ERR_VAL;
+ }
+
+ b = *buf;
+ do {
+ if (b & 0x80) {
+ *bit_value |= (1 << bits_processed);
+ }
+ bits_processed++;
+ b <<= 1;
+ }
+ while ((bits_processed & 0x07) != 0); /* &0x07 -> % 8 */
+ } else {
+ bits_processed += 8;
+ }
+
+ buf_len--;
+ buf++;
+ }
+
+ return ERR_OK;
+}
+
+err_t
+snmp_decode_truthvalue(const s32_t *asn1_value, u8_t *bool_value)
+{
+ /* defined by RFC1443:
+ TruthValue ::= TEXTUAL-CONVENTION
+ STATUS current
+ DESCRIPTION
+ "Represents a boolean value."
+ SYNTAX INTEGER { true(1), false(2) }
+ */
+
+ if ((asn1_value == NULL) || (bool_value == NULL)) {
+ return ERR_ARG;
+ }
+
+ if (*asn1_value == 1) {
+ *bool_value = 1;
+ } else if (*asn1_value == 2) {
+ *bool_value = 0;
+ } else {
+ return ERR_VAL;
+ }
+
+ return ERR_OK;
+}
+
+/**
+ * Encodes BITS pseudotype value into ASN.1 OctetString.
+ *
+ * @note Because BITS pseudo type is encoded as OCTET STRING, it cannot directly
+ * be encoded/decoded by the agent. Instead call this function as required from
+ * get/test/set methods.
+ *
+ * @param buf points to a buffer where the resulting ASN1 octet string is stored to
+ * @param buf_len max length of the bufffer
+ * @param bit_value Bit value to encode with Bit0 == LSB
+ * @param bit_count Number of possible bits for the bit value (according to rfc we have to send all bits independant from their truth value)
+ * @return number of bytes used from buffer to store the resulting OctetString
+ */
+u8_t
+snmp_encode_bits(u8_t *buf, u32_t buf_len, u32_t bit_value, u8_t bit_count)
+{
+ u8_t len = 0;
+ u8_t min_bytes = (bit_count + 7) >> 3; /* >>3 -> / 8 */
+
+ while ((buf_len > 0) && (bit_value != 0x00)) {
+ s8_t i = 7;
+ *buf = 0x00;
+ while (i >= 0) {
+ if (bit_value & 0x01) {
+ *buf |= 0x01;
+ }
+
+ if (i > 0) {
+ *buf <<= 1;
+ }
+
+ bit_value >>= 1;
+ i--;
+ }
+
+ buf++;
+ buf_len--;
+ len++;
+ }
+
+ if (len < min_bytes) {
+ buf += len;
+ buf_len -= len;
+
+ while ((len < min_bytes) && (buf_len > 0)) {
+ *buf = 0x00;
+ buf++;
+ buf_len--;
+ len++;
+ }
+ }
+
+ return len;
+}
+
+u8_t
+snmp_encode_truthvalue(s32_t *asn1_value, u32_t bool_value)
+{
+ /* defined by RFC1443:
+ TruthValue ::= TEXTUAL-CONVENTION
+ STATUS current
+ DESCRIPTION
+ "Represents a boolean value."
+ SYNTAX INTEGER { true(1), false(2) }
+ */
+
+ if (asn1_value == NULL) {
+ return 0;
+ }
+
+ if (bool_value) {
+ *asn1_value = 1; /* defined by RFC1443 */
+ } else {
+ *asn1_value = 2; /* defined by RFC1443 */
+ }
+
+ return sizeof(s32_t);
+}
+
+#endif /* LWIP_SNMP */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_core_priv.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_core_priv.h
new file mode 100644
index 0000000..5552177
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_core_priv.h
@@ -0,0 +1,76 @@
+/*
+ * 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: Martin Hentschel <info@cl-soft.de>
+ *
+ */
+
+#ifndef LWIP_HDR_APPS_SNMP_CORE_PRIV_H
+#define LWIP_HDR_APPS_SNMP_CORE_PRIV_H
+
+#include "lwip/apps/snmp_opts.h"
+
+#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/apps/snmp_core.h"
+#include "snmp_asn1.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* (outdated) SNMPv1 error codes
+ * shall not be used by MIBS anymore, nevertheless required from core for properly answering a v1 request
+ */
+#define SNMP_ERR_NOSUCHNAME 2
+#define SNMP_ERR_BADVALUE 3
+#define SNMP_ERR_READONLY 4
+/* error codes which are internal and shall not be used by MIBS
+ * shall not be used by MIBS anymore, nevertheless required from core for properly answering a v1 request
+ */
+#define SNMP_ERR_TOOBIG 1
+#define SNMP_ERR_AUTHORIZATIONERROR 16
+#define SNMP_ERR_NOSUCHOBJECT SNMP_VARBIND_EXCEPTION_OFFSET + SNMP_ASN1_CONTEXT_VARBIND_NO_SUCH_OBJECT
+#define SNMP_ERR_ENDOFMIBVIEW SNMP_VARBIND_EXCEPTION_OFFSET + SNMP_ASN1_CONTEXT_VARBIND_END_OF_MIB_VIEW
+
+
+const struct snmp_node* snmp_mib_tree_resolve_exact(const struct snmp_mib *mib, const u32_t *oid, u8_t oid_len, u8_t* oid_instance_len);
+const struct snmp_node* snmp_mib_tree_resolve_next(const struct snmp_mib *mib, const u32_t *oid, u8_t oid_len, struct snmp_obj_id* oidret);
+
+typedef u8_t (*snmp_validate_node_instance_method)(struct snmp_node_instance*, void*);
+
+u8_t snmp_get_node_instance_from_oid(const u32_t *oid, u8_t oid_len, struct snmp_node_instance* node_instance);
+u8_t snmp_get_next_node_instance_from_oid(const u32_t *oid, u8_t oid_len, snmp_validate_node_instance_method validate_node_instance_method, void* validate_node_instance_arg, struct snmp_obj_id* node_oid, struct snmp_node_instance* node_instance);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_SNMP */
+
+#endif /* LWIP_HDR_APPS_SNMP_CORE_PRIV_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_mib2.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_mib2.c
new file mode 100644
index 0000000..9d8c43c
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_mib2.c
@@ -0,0 +1,116 @@
+/**
+ * @file
+ * Management Information Base II (RFC1213) objects and 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: Dirk Ziegelmeier <dziegel@gmx.de>
+ * Christiaan Simons <christiaan.simons@axon.tv>
+ */
+
+/**
+ * @defgroup snmp_mib2 MIB2
+ * @ingroup snmp
+ */
+
+#include "lwip/apps/snmp_opts.h"
+
+#if LWIP_SNMP && SNMP_LWIP_MIB2 /* don't build if not configured for use in lwipopts.h */
+
+#if !LWIP_STATS
+#error LWIP_SNMP MIB2 needs LWIP_STATS (for MIB2)
+#endif
+#if !MIB2_STATS
+#error LWIP_SNMP MIB2 needs MIB2_STATS (for MIB2)
+#endif
+
+#include "lwip/snmp.h"
+#include "lwip/apps/snmp.h"
+#include "lwip/apps/snmp_core.h"
+#include "lwip/apps/snmp_mib2.h"
+#include "lwip/apps/snmp_scalar.h"
+
+#if SNMP_USE_NETCONN
+#include "lwip/tcpip.h"
+#include "lwip/priv/tcpip_priv.h"
+void
+snmp_mib2_lwip_synchronizer(snmp_threadsync_called_fn fn, void* arg)
+{
+#if LWIP_TCPIP_CORE_LOCKING
+ LOCK_TCPIP_CORE();
+ fn(arg);
+ UNLOCK_TCPIP_CORE();
+#else
+ tcpip_callback(fn, arg);
+#endif
+}
+
+struct snmp_threadsync_instance snmp_mib2_lwip_locks;
+#endif
+
+/* 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) */
+
+/* --- mib-2 .1.3.6.1.2.1 ----------------------------------------------------- */
+extern const struct snmp_scalar_array_node snmp_mib2_snmp_root;
+extern const struct snmp_tree_node snmp_mib2_udp_root;
+extern const struct snmp_tree_node snmp_mib2_tcp_root;
+extern const struct snmp_scalar_array_node snmp_mib2_icmp_root;
+extern const struct snmp_tree_node snmp_mib2_interface_root;
+extern const struct snmp_scalar_array_node snmp_mib2_system_node;
+extern const struct snmp_tree_node snmp_mib2_at_root;
+extern const struct snmp_tree_node snmp_mib2_ip_root;
+
+static const struct snmp_node* const mib2_nodes[] = {
+ &snmp_mib2_system_node.node.node,
+ &snmp_mib2_interface_root.node,
+#if LWIP_ARP && LWIP_IPV4
+ &snmp_mib2_at_root.node,
+#endif /* LWIP_ARP && LWIP_IPV4 */
+#if LWIP_IPV4
+ &snmp_mib2_ip_root.node,
+#endif /* LWIP_IPV4 */
+#if LWIP_ICMP
+ &snmp_mib2_icmp_root.node.node,
+#endif /* LWIP_ICMP */
+#if LWIP_TCP
+ &snmp_mib2_tcp_root.node,
+#endif /* LWIP_TCP */
+#if LWIP_UDP
+ &snmp_mib2_udp_root.node,
+#endif /* LWIP_UDP */
+ &snmp_mib2_snmp_root.node.node
+};
+
+static const struct snmp_tree_node mib2_root = SNMP_CREATE_TREE_NODE(1, mib2_nodes);
+
+static const u32_t mib2_base_oid_arr[] = { 1,3,6,1,2,1 };
+const struct snmp_mib mib2 = SNMP_MIB_CREATE(mib2_base_oid_arr, &mib2_root.node);
+
+#endif /* LWIP_SNMP && SNMP_LWIP_MIB2 */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_mib2_icmp.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_mib2_icmp.c
new file mode 100644
index 0000000..995bd32
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_mib2_icmp.c
@@ -0,0 +1,182 @@
+/**
+ * @file
+ * Management Information Base II (RFC1213) ICMP objects and 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: Dirk Ziegelmeier <dziegel@gmx.de>
+ * Christiaan Simons <christiaan.simons@axon.tv>
+ */
+
+#include "lwip/snmp.h"
+#include "lwip/apps/snmp.h"
+#include "lwip/apps/snmp_core.h"
+#include "lwip/apps/snmp_mib2.h"
+#include "lwip/apps/snmp_table.h"
+#include "lwip/apps/snmp_scalar.h"
+#include "lwip/icmp.h"
+#include "lwip/stats.h"
+
+#if LWIP_SNMP && SNMP_LWIP_MIB2 && LWIP_ICMP
+
+#if SNMP_USE_NETCONN
+#define SYNC_NODE_NAME(node_name) node_name ## _synced
+#define CREATE_LWIP_SYNC_NODE(oid, node_name) \
+ static const struct snmp_threadsync_node node_name ## _synced = SNMP_CREATE_THREAD_SYNC_NODE(oid, &node_name.node, &snmp_mib2_lwip_locks);
+#else
+#define SYNC_NODE_NAME(node_name) node_name
+#define CREATE_LWIP_SYNC_NODE(oid, node_name)
+#endif
+
+/* --- icmp .1.3.6.1.2.1.5 ----------------------------------------------------- */
+
+static s16_t
+icmp_get_value(const struct snmp_scalar_array_node_def *node, void *value)
+{
+ u32_t *uint_ptr = (u32_t*)value;
+
+ switch (node->oid) {
+ case 1: /* icmpInMsgs */
+ *uint_ptr = STATS_GET(mib2.icmpinmsgs);
+ return sizeof(*uint_ptr);
+ case 2: /* icmpInErrors */
+ *uint_ptr = STATS_GET(mib2.icmpinerrors);
+ return sizeof(*uint_ptr);
+ case 3: /* icmpInDestUnreachs */
+ *uint_ptr = STATS_GET(mib2.icmpindestunreachs);
+ return sizeof(*uint_ptr);
+ case 4: /* icmpInTimeExcds */
+ *uint_ptr = STATS_GET(mib2.icmpintimeexcds);
+ return sizeof(*uint_ptr);
+ case 5: /* icmpInParmProbs */
+ *uint_ptr = STATS_GET(mib2.icmpinparmprobs);
+ return sizeof(*uint_ptr);
+ case 6: /* icmpInSrcQuenchs */
+ *uint_ptr = STATS_GET(mib2.icmpinsrcquenchs);
+ return sizeof(*uint_ptr);
+ case 7: /* icmpInRedirects */
+ *uint_ptr = STATS_GET(mib2.icmpinredirects);
+ return sizeof(*uint_ptr);
+ case 8: /* icmpInEchos */
+ *uint_ptr = STATS_GET(mib2.icmpinechos);
+ return sizeof(*uint_ptr);
+ case 9: /* icmpInEchoReps */
+ *uint_ptr = STATS_GET(mib2.icmpinechoreps);
+ return sizeof(*uint_ptr);
+ case 10: /* icmpInTimestamps */
+ *uint_ptr = STATS_GET(mib2.icmpintimestamps);
+ return sizeof(*uint_ptr);
+ case 11: /* icmpInTimestampReps */
+ *uint_ptr = STATS_GET(mib2.icmpintimestampreps);
+ return sizeof(*uint_ptr);
+ case 12: /* icmpInAddrMasks */
+ *uint_ptr = STATS_GET(mib2.icmpinaddrmasks);
+ return sizeof(*uint_ptr);
+ case 13: /* icmpInAddrMaskReps */
+ *uint_ptr = STATS_GET(mib2.icmpinaddrmaskreps);
+ return sizeof(*uint_ptr);
+ case 14: /* icmpOutMsgs */
+ *uint_ptr = STATS_GET(mib2.icmpoutmsgs);
+ return sizeof(*uint_ptr);
+ case 15: /* icmpOutErrors */
+ *uint_ptr = STATS_GET(mib2.icmpouterrors);
+ return sizeof(*uint_ptr);
+ case 16: /* icmpOutDestUnreachs */
+ *uint_ptr = STATS_GET(mib2.icmpoutdestunreachs);
+ return sizeof(*uint_ptr);
+ case 17: /* icmpOutTimeExcds */
+ *uint_ptr = STATS_GET(mib2.icmpouttimeexcds);
+ return sizeof(*uint_ptr);
+ case 18: /* icmpOutParmProbs: not supported -> always 0 */
+ *uint_ptr = 0;
+ return sizeof(*uint_ptr);
+ case 19: /* icmpOutSrcQuenchs: not supported -> always 0 */
+ *uint_ptr = 0;
+ return sizeof(*uint_ptr);
+ case 20: /* icmpOutRedirects: not supported -> always 0 */
+ *uint_ptr = 0;
+ return sizeof(*uint_ptr);
+ case 21: /* icmpOutEchos */
+ *uint_ptr = STATS_GET(mib2.icmpoutechos);
+ return sizeof(*uint_ptr);
+ case 22: /* icmpOutEchoReps */
+ *uint_ptr = STATS_GET(mib2.icmpoutechoreps);
+ return sizeof(*uint_ptr);
+ case 23: /* icmpOutTimestamps: not supported -> always 0 */
+ *uint_ptr = 0;
+ return sizeof(*uint_ptr);
+ case 24: /* icmpOutTimestampReps: not supported -> always 0 */
+ *uint_ptr = 0;
+ return sizeof(*uint_ptr);
+ case 25: /* icmpOutAddrMasks: not supported -> always 0 */
+ *uint_ptr = 0;
+ return sizeof(*uint_ptr);
+ case 26: /* icmpOutAddrMaskReps: not supported -> always 0 */
+ *uint_ptr = 0;
+ return sizeof(*uint_ptr);
+ default:
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("icmp_get_value(): unknown id: %"S32_F"\n", node->oid));
+ break;
+ }
+
+ return 0;
+}
+
+
+static const struct snmp_scalar_array_node_def icmp_nodes[] = {
+ { 1, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+ { 2, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+ { 3, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+ { 4, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+ { 5, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+ { 6, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+ { 7, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+ { 8, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+ { 9, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+ {10, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+ {11, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+ {12, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+ {13, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+ {14, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+ {15, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+ {16, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+ {17, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+ {18, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+ {19, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+ {20, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+ {21, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+ {22, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+ {23, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+ {24, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+ {25, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
+ {26, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}
+};
+
+const struct snmp_scalar_array_node snmp_mib2_icmp_root = SNMP_SCALAR_CREATE_ARRAY_NODE(5, icmp_nodes, icmp_get_value, NULL, NULL);
+
+#endif /* LWIP_SNMP && SNMP_LWIP_MIB2 && LWIP_ICMP */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_mib2_interfaces.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_mib2_interfaces.c
new file mode 100644
index 0000000..979b507
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_mib2_interfaces.c
@@ -0,0 +1,375 @@
+/**
+ * @file
+ * Management Information Base II (RFC1213) INTERFACES objects and 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: Dirk Ziegelmeier <dziegel@gmx.de>
+ * Christiaan Simons <christiaan.simons@axon.tv>
+ */
+
+#include "lwip/snmp.h"
+#include "lwip/apps/snmp.h"
+#include "lwip/apps/snmp_core.h"
+#include "lwip/apps/snmp_mib2.h"
+#include "lwip/apps/snmp_table.h"
+#include "lwip/apps/snmp_scalar.h"
+#include "lwip/netif.h"
+#include "lwip/stats.h"
+
+#include <string.h>
+
+#if LWIP_SNMP && SNMP_LWIP_MIB2
+
+#if SNMP_USE_NETCONN
+#define SYNC_NODE_NAME(node_name) node_name ## _synced
+#define CREATE_LWIP_SYNC_NODE(oid, node_name) \
+ static const struct snmp_threadsync_node node_name ## _synced = SNMP_CREATE_THREAD_SYNC_NODE(oid, &node_name.node, &snmp_mib2_lwip_locks);
+#else
+#define SYNC_NODE_NAME(node_name) node_name
+#define CREATE_LWIP_SYNC_NODE(oid, node_name)
+#endif
+
+
+/* --- interfaces .1.3.6.1.2.1.2 ----------------------------------------------------- */
+
+static s16_t
+interfaces_get_value(struct snmp_node_instance* instance, void* value)
+{
+ if (instance->node->oid == 1) {
+ s32_t *sint_ptr = (s32_t*)value;
+ s32_t num_netifs = 0;
+
+ struct netif *netif = netif_list;
+ while (netif != NULL) {
+ num_netifs++;
+ netif = netif->next;
+ }
+
+ *sint_ptr = num_netifs;
+ return sizeof(*sint_ptr);
+ }
+
+ return 0;
+}
+
+/* list of allowed value ranges for incoming OID */
+static const struct snmp_oid_range interfaces_Table_oid_ranges[] = {
+ { 1, 0xff } /* netif->num is u8_t */
+};
+
+static const u8_t iftable_ifOutQLen = 0;
+
+static const u8_t iftable_ifOperStatus_up = 1;
+static const u8_t iftable_ifOperStatus_down = 2;
+
+static const u8_t iftable_ifAdminStatus_up = 1;
+static const u8_t iftable_ifAdminStatus_lowerLayerDown = 7;
+static const u8_t iftable_ifAdminStatus_down = 2;
+
+static snmp_err_t
+interfaces_Table_get_cell_instance(const u32_t* column, const u32_t* row_oid, u8_t row_oid_len, struct snmp_node_instance* cell_instance)
+{
+ u32_t ifIndex;
+ struct netif *netif;
+
+ LWIP_UNUSED_ARG(column);
+
+ /* check if incoming OID length and if values are in plausible range */
+ if (!snmp_oid_in_range(row_oid, row_oid_len, interfaces_Table_oid_ranges, LWIP_ARRAYSIZE(interfaces_Table_oid_ranges))) {
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ /* get netif index from incoming OID */
+ ifIndex = row_oid[0];
+
+ /* find netif with index */
+ netif = netif_list;
+ while (netif != NULL) {
+ if (netif_to_num(netif) == ifIndex) {
+ /* store netif pointer for subsequent operations (get/test/set) */
+ cell_instance->reference.ptr = netif;
+ return SNMP_ERR_NOERROR;
+ }
+ netif = netif->next;
+ }
+
+ /* not found */
+ return SNMP_ERR_NOSUCHINSTANCE;
+}
+
+static snmp_err_t
+interfaces_Table_get_next_cell_instance(const u32_t* column, struct snmp_obj_id* row_oid, struct snmp_node_instance* cell_instance)
+{
+ struct netif *netif;
+ struct snmp_next_oid_state state;
+ u32_t result_temp[LWIP_ARRAYSIZE(interfaces_Table_oid_ranges)];
+
+ LWIP_UNUSED_ARG(column);
+
+ /* init struct to search next oid */
+ snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(interfaces_Table_oid_ranges));
+
+ /* iterate over all possible OIDs to find the next one */
+ netif = netif_list;
+ while (netif != NULL) {
+ u32_t test_oid[LWIP_ARRAYSIZE(interfaces_Table_oid_ranges)];
+ test_oid[0] = netif_to_num(netif);
+
+ /* check generated OID: is it a candidate for the next one? */
+ snmp_next_oid_check(&state, test_oid, LWIP_ARRAYSIZE(interfaces_Table_oid_ranges), netif);
+
+ netif = netif->next;
+ }
+
+ /* did we find a next one? */
+ if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) {
+ snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len);
+ /* store netif pointer for subsequent operations (get/test/set) */
+ cell_instance->reference.ptr = /* (struct netif*) */state.reference;
+ return SNMP_ERR_NOERROR;
+ }
+
+ /* not found */
+ return SNMP_ERR_NOSUCHINSTANCE;
+}
+
+static s16_t
+interfaces_Table_get_value(struct snmp_node_instance* instance, void* value)
+{
+ struct netif *netif = (struct netif*)instance->reference.ptr;
+ u32_t* value_u32 = (u32_t*)value;
+ s32_t* value_s32 = (s32_t*)value;
+ u16_t value_len;
+
+ switch (SNMP_TABLE_GET_COLUMN_FROM_OID(instance->instance_oid.id))
+ {
+ case 1: /* ifIndex */
+ *value_s32 = netif_to_num(netif);
+ value_len = sizeof(*value_s32);
+ break;
+ case 2: /* ifDescr */
+ value_len = sizeof(netif->name);
+ MEMCPY(value, netif->name, value_len);
+ break;
+ case 3: /* ifType */
+ *value_s32 = netif->link_type;
+ value_len = sizeof(*value_s32);
+ break;
+ case 4: /* ifMtu */
+ *value_s32 = netif->mtu;
+ value_len = sizeof(*value_s32);
+ break;
+ case 5: /* ifSpeed */
+ *value_u32 = netif->link_speed;
+ value_len = sizeof(*value_u32);
+ break;
+ case 6: /* ifPhysAddress */
+ value_len = sizeof(netif->hwaddr);
+ MEMCPY(value, &netif->hwaddr, value_len);
+ break;
+ case 7: /* ifAdminStatus */
+ if (netif_is_up(netif)) {
+ *value_s32 = iftable_ifOperStatus_up;
+ } else {
+ *value_s32 = iftable_ifOperStatus_down;
+ }
+ value_len = sizeof(*value_s32);
+ break;
+ case 8: /* ifOperStatus */
+ if (netif_is_up(netif)) {
+ if (netif_is_link_up(netif)) {
+ *value_s32 = iftable_ifAdminStatus_up;
+ } else {
+ *value_s32 = iftable_ifAdminStatus_lowerLayerDown;
+ }
+ } else {
+ *value_s32 = iftable_ifAdminStatus_down;
+ }
+ value_len = sizeof(*value_s32);
+ break;
+ case 9: /* ifLastChange */
+ *value_u32 = netif->ts;
+ value_len = sizeof(*value_u32);
+ break;
+ case 10: /* ifInOctets */
+ *value_u32 = netif->mib2_counters.ifinoctets;
+ value_len = sizeof(*value_u32);
+ break;
+ case 11: /* ifInUcastPkts */
+ *value_u32 = netif->mib2_counters.ifinucastpkts;
+ value_len = sizeof(*value_u32);
+ break;
+ case 12: /* ifInNUcastPkts */
+ *value_u32 = netif->mib2_counters.ifinnucastpkts;
+ value_len = sizeof(*value_u32);
+ break;
+ case 13: /* ifInDiscards */
+ *value_u32 = netif->mib2_counters.ifindiscards;
+ value_len = sizeof(*value_u32);
+ break;
+ case 14: /* ifInErrors */
+ *value_u32 = netif->mib2_counters.ifinerrors;
+ value_len = sizeof(*value_u32);
+ break;
+ case 15: /* ifInUnkownProtos */
+ *value_u32 = netif->mib2_counters.ifinunknownprotos;
+ value_len = sizeof(*value_u32);
+ break;
+ case 16: /* ifOutOctets */
+ *value_u32 = netif->mib2_counters.ifoutoctets;
+ value_len = sizeof(*value_u32);
+ break;
+ case 17: /* ifOutUcastPkts */
+ *value_u32 = netif->mib2_counters.ifoutucastpkts;
+ value_len = sizeof(*value_u32);
+ break;
+ case 18: /* ifOutNUcastPkts */
+ *value_u32 = netif->mib2_counters.ifoutnucastpkts;
+ value_len = sizeof(*value_u32);
+ break;
+ case 19: /* ifOutDiscarts */
+ *value_u32 = netif->mib2_counters.ifoutdiscards;
+ value_len = sizeof(*value_u32);
+ break;
+ case 20: /* ifOutErrors */
+ *value_u32 = netif->mib2_counters.ifouterrors;
+ value_len = sizeof(*value_u32);
+ break;
+ case 21: /* ifOutQLen */
+ *value_u32 = iftable_ifOutQLen;
+ value_len = sizeof(*value_u32);
+ break;
+ /** @note returning zeroDotZero (0.0) no media specific MIB support */
+ case 22: /* ifSpecific */
+ value_len = snmp_zero_dot_zero.len * sizeof(u32_t);
+ MEMCPY(value, snmp_zero_dot_zero.id, value_len);
+ break;
+ default:
+ return 0;
+ }
+
+ return value_len;
+}
+
+#if !SNMP_SAFE_REQUESTS
+
+static snmp_err_t
+interfaces_Table_set_test(struct snmp_node_instance* instance, u16_t len, void *value)
+{
+ s32_t *sint_ptr = (s32_t*)value;
+
+ /* stack should never call this method for another column,
+ because all other columns are set to readonly */
+ LWIP_ASSERT("Invalid column", (SNMP_TABLE_GET_COLUMN_FROM_OID(instance->instance_oid.id) == 7));
+ LWIP_UNUSED_ARG(len);
+
+ if (*sint_ptr == 1 || *sint_ptr == 2) {
+ return SNMP_ERR_NOERROR;
+ }
+
+ return SNMP_ERR_WRONGVALUE;
+}
+
+static snmp_err_t
+interfaces_Table_set_value(struct snmp_node_instance* instance, u16_t len, void *value)
+{
+ struct netif *netif = (struct netif*)instance->reference.ptr;
+ s32_t *sint_ptr = (s32_t*)value;
+
+ /* stack should never call this method for another column,
+ because all other columns are set to readonly */
+ LWIP_ASSERT("Invalid column", (SNMP_TABLE_GET_COLUMN_FROM_OID(instance->instance_oid.id) == 7));
+ LWIP_UNUSED_ARG(len);
+
+ if (*sint_ptr == 1) {
+ netif_set_up(netif);
+ } else if (*sint_ptr == 2) {
+ netif_set_down(netif);
+ }
+
+ return SNMP_ERR_NOERROR;
+}
+
+#endif /* SNMP_SAFE_REQUESTS */
+
+static const struct snmp_scalar_node interfaces_Number = SNMP_SCALAR_CREATE_NODE_READONLY(1, SNMP_ASN1_TYPE_INTEGER, interfaces_get_value);
+
+static const struct snmp_table_col_def interfaces_Table_columns[] = {
+ { 1, SNMP_ASN1_TYPE_INTEGER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifIndex */
+ { 2, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifDescr */
+ { 3, SNMP_ASN1_TYPE_INTEGER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifType */
+ { 4, SNMP_ASN1_TYPE_INTEGER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifMtu */
+ { 5, SNMP_ASN1_TYPE_GAUGE, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifSpeed */
+ { 6, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifPhysAddress */
+#if !SNMP_SAFE_REQUESTS
+ { 7, SNMP_ASN1_TYPE_INTEGER, SNMP_NODE_INSTANCE_READ_WRITE }, /* ifAdminStatus */
+#else
+ { 7, SNMP_ASN1_TYPE_INTEGER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifAdminStatus */
+#endif
+ { 8, SNMP_ASN1_TYPE_INTEGER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOperStatus */
+ { 9, SNMP_ASN1_TYPE_TIMETICKS, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifLastChange */
+ { 10, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifInOctets */
+ { 11, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifInUcastPkts */
+ { 12, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifInNUcastPkts */
+ { 13, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifInDiscarts */
+ { 14, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifInErrors */
+ { 15, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifInUnkownProtos */
+ { 16, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOutOctets */
+ { 17, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOutUcastPkts */
+ { 18, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOutNUcastPkts */
+ { 19, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOutDiscarts */
+ { 20, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOutErrors */
+ { 21, SNMP_ASN1_TYPE_GAUGE, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOutQLen */
+ { 22, SNMP_ASN1_TYPE_OBJECT_ID, SNMP_NODE_INSTANCE_READ_ONLY } /* ifSpecific */
+};
+
+#if !SNMP_SAFE_REQUESTS
+static const struct snmp_table_node interfaces_Table = SNMP_TABLE_CREATE(
+ 2, interfaces_Table_columns,
+ interfaces_Table_get_cell_instance, interfaces_Table_get_next_cell_instance,
+ interfaces_Table_get_value, interfaces_Table_set_test, interfaces_Table_set_value);
+#else
+static const struct snmp_table_node interfaces_Table = SNMP_TABLE_CREATE(
+ 2, interfaces_Table_columns,
+ interfaces_Table_get_cell_instance, interfaces_Table_get_next_cell_instance,
+ interfaces_Table_get_value, NULL, NULL);
+#endif
+
+/* the following nodes access variables in LWIP stack from SNMP worker thread and must therefore be synced to LWIP (TCPIP) thread */
+CREATE_LWIP_SYNC_NODE(1, interfaces_Number)
+CREATE_LWIP_SYNC_NODE(2, interfaces_Table)
+
+static const struct snmp_node* const interface_nodes[] = {
+ &SYNC_NODE_NAME(interfaces_Number).node.node,
+ &SYNC_NODE_NAME(interfaces_Table).node.node
+};
+
+const struct snmp_tree_node snmp_mib2_interface_root = SNMP_CREATE_TREE_NODE(2, interface_nodes);
+
+#endif /* LWIP_SNMP && SNMP_LWIP_MIB2 */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_mib2_ip.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_mib2_ip.c
new file mode 100644
index 0000000..4f05180
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_mib2_ip.c
@@ -0,0 +1,743 @@
+/**
+ * @file
+ * Management Information Base II (RFC1213) IP objects and 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: Dirk Ziegelmeier <dziegel@gmx.de>
+ * Christiaan Simons <christiaan.simons@axon.tv>
+ */
+
+#include "lwip/snmp.h"
+#include "lwip/apps/snmp.h"
+#include "lwip/apps/snmp_core.h"
+#include "lwip/apps/snmp_mib2.h"
+#include "lwip/apps/snmp_table.h"
+#include "lwip/apps/snmp_scalar.h"
+#include "lwip/stats.h"
+#include "lwip/netif.h"
+#include "lwip/ip.h"
+#include "lwip/etharp.h"
+
+#if LWIP_SNMP && SNMP_LWIP_MIB2
+
+#if SNMP_USE_NETCONN
+#define SYNC_NODE_NAME(node_name) node_name ## _synced
+#define CREATE_LWIP_SYNC_NODE(oid, node_name) \
+ static const struct snmp_threadsync_node node_name ## _synced = SNMP_CREATE_THREAD_SYNC_NODE(oid, &node_name.node, &snmp_mib2_lwip_locks);
+#else
+#define SYNC_NODE_NAME(node_name) node_name
+#define CREATE_LWIP_SYNC_NODE(oid, node_name)
+#endif
+
+#if LWIP_IPV4
+/* --- ip .1.3.6.1.2.1.4 ----------------------------------------------------- */
+
+static s16_t
+ip_get_value(struct snmp_node_instance* instance, void* value)
+{
+ s32_t* sint_ptr = (s32_t*)value;
+ u32_t* uint_ptr = (u32_t*)value;
+
+ switch (instance->node->oid) {
+ case 1: /* ipForwarding */
+#if IP_FORWARD
+ /* forwarding */
+ *sint_ptr = 1;
+#else
+ /* not-forwarding */
+ *sint_ptr = 2;
+#endif
+ return sizeof(*sint_ptr);
+ case 2: /* ipDefaultTTL */
+ *sint_ptr = IP_DEFAULT_TTL;
+ return sizeof(*sint_ptr);
+ case 3: /* ipInReceives */
+ *uint_ptr = STATS_GET(mib2.ipinreceives);
+ return sizeof(*uint_ptr);
+ case 4: /* ipInHdrErrors */
+ *uint_ptr = STATS_GET(mib2.ipinhdrerrors);
+ return sizeof(*uint_ptr);
+ case 5: /* ipInAddrErrors */
+ *uint_ptr = STATS_GET(mib2.ipinaddrerrors);
+ return sizeof(*uint_ptr);
+ case 6: /* ipForwDatagrams */
+ *uint_ptr = STATS_GET(mib2.ipforwdatagrams);
+ return sizeof(*uint_ptr);
+ case 7: /* ipInUnknownProtos */
+ *uint_ptr = STATS_GET(mib2.ipinunknownprotos);
+ return sizeof(*uint_ptr);
+ case 8: /* ipInDiscards */
+ *uint_ptr = STATS_GET(mib2.ipindiscards);
+ return sizeof(*uint_ptr);
+ case 9: /* ipInDelivers */
+ *uint_ptr = STATS_GET(mib2.ipindelivers);
+ return sizeof(*uint_ptr);
+ case 10: /* ipOutRequests */
+ *uint_ptr = STATS_GET(mib2.ipoutrequests);
+ return sizeof(*uint_ptr);
+ case 11: /* ipOutDiscards */
+ *uint_ptr = STATS_GET(mib2.ipoutdiscards);
+ return sizeof(*uint_ptr);
+ case 12: /* ipOutNoRoutes */
+ *uint_ptr = STATS_GET(mib2.ipoutnoroutes);
+ return sizeof(*uint_ptr);
+ case 13: /* ipReasmTimeout */
+#if IP_REASSEMBLY
+ *sint_ptr = IP_REASS_MAXAGE;
+#else
+ *sint_ptr = 0;
+#endif
+ return sizeof(*sint_ptr);
+ case 14: /* ipReasmReqds */
+ *uint_ptr = STATS_GET(mib2.ipreasmreqds);
+ return sizeof(*uint_ptr);
+ case 15: /* ipReasmOKs */
+ *uint_ptr = STATS_GET(mib2.ipreasmoks);
+ return sizeof(*uint_ptr);
+ case 16: /* ipReasmFails */
+ *uint_ptr = STATS_GET(mib2.ipreasmfails);
+ return sizeof(*uint_ptr);
+ case 17: /* ipFragOKs */
+ *uint_ptr = STATS_GET(mib2.ipfragoks);
+ return sizeof(*uint_ptr);
+ case 18: /* ipFragFails */
+ *uint_ptr = STATS_GET(mib2.ipfragfails);
+ return sizeof(*uint_ptr);
+ case 19: /* ipFragCreates */
+ *uint_ptr = STATS_GET(mib2.ipfragcreates);
+ return sizeof(*uint_ptr);
+ case 23: /* ipRoutingDiscards: not supported -> always 0 */
+ *uint_ptr = 0;
+ return sizeof(*uint_ptr);
+ default:
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_get_value(): unknown id: %"S32_F"\n", instance->node->oid));
+ break;
+ }
+
+ return 0;
+}
+
+/**
+ * Test ip object value before setting.
+ *
+ * @param instance node instance
+ * @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 snmp_err_t
+ip_set_test(struct snmp_node_instance* instance, u16_t len, void *value)
+{
+ snmp_err_t ret = SNMP_ERR_WRONGVALUE;
+ s32_t *sint_ptr = (s32_t*)value;
+
+ LWIP_UNUSED_ARG(len);
+ switch (instance->node->oid) {
+ case 1: /* ipForwarding */
+#if IP_FORWARD
+ /* forwarding */
+ if (*sint_ptr == 1)
+#else
+ /* not-forwarding */
+ if (*sint_ptr == 2)
+#endif
+ {
+ ret = SNMP_ERR_NOERROR;
+ }
+ break;
+ case 2: /* ipDefaultTTL */
+ if (*sint_ptr == IP_DEFAULT_TTL) {
+ ret = SNMP_ERR_NOERROR;
+ }
+ break;
+ default:
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_set_test(): unknown id: %"S32_F"\n", instance->node->oid));
+ break;
+ }
+
+ return ret;
+}
+
+static snmp_err_t
+ip_set_value(struct snmp_node_instance* instance, u16_t len, void *value)
+{
+ LWIP_UNUSED_ARG(instance);
+ LWIP_UNUSED_ARG(len);
+ LWIP_UNUSED_ARG(value);
+ /* nothing to do here because in set_test we only accept values being the same as our own stored value -> no need to store anything */
+ return SNMP_ERR_NOERROR;
+}
+
+/* --- ipAddrTable --- */
+
+/* list of allowed value ranges for incoming OID */
+static const struct snmp_oid_range ip_AddrTable_oid_ranges[] = {
+ { 0, 0xff }, /* IP A */
+ { 0, 0xff }, /* IP B */
+ { 0, 0xff }, /* IP C */
+ { 0, 0xff } /* IP D */
+};
+
+static snmp_err_t
+ip_AddrTable_get_cell_value_core(struct netif *netif, const u32_t* column, union snmp_variant_value* value, u32_t* value_len)
+{
+ LWIP_UNUSED_ARG(value_len);
+
+ switch (*column) {
+ case 1: /* ipAdEntAddr */
+ value->u32 = netif_ip4_addr(netif)->addr;
+ break;
+ case 2: /* ipAdEntIfIndex */
+ value->u32 = netif_to_num(netif);
+ break;
+ case 3: /* ipAdEntNetMask */
+ value->u32 = netif_ip4_netmask(netif)->addr;
+ break;
+ case 4: /* ipAdEntBcastAddr */
+ /* lwIP oddity, there's no broadcast
+ address in the netif we can rely on */
+ value->u32 = IPADDR_BROADCAST & 1;
+ break;
+ case 5: /* ipAdEntReasmMaxSize */
+#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...
+ */
+ value->u32 = (IP_HLEN + ((IP_REASS_MAX_PBUFS/2) *
+ (PBUF_POOL_BUFSIZE - PBUF_LINK_ENCAPSULATION_HLEN - 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 */
+ value->u32 = 0;
+#endif
+ break;
+ default:
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ return SNMP_ERR_NOERROR;
+}
+
+static snmp_err_t
+ip_AddrTable_get_cell_value(const u32_t* column, const u32_t* row_oid, u8_t row_oid_len, union snmp_variant_value* value, u32_t* value_len)
+{
+ ip4_addr_t ip;
+ struct netif *netif;
+
+ /* check if incoming OID length and if values are in plausible range */
+ if (!snmp_oid_in_range(row_oid, row_oid_len, ip_AddrTable_oid_ranges, LWIP_ARRAYSIZE(ip_AddrTable_oid_ranges))) {
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ /* get IP from incoming OID */
+ snmp_oid_to_ip4(&row_oid[0], &ip); /* we know it succeeds because of oid_in_range check above */
+
+ /* find netif with requested ip */
+ netif = netif_list;
+ while (netif != NULL) {
+ if (ip4_addr_cmp(&ip, netif_ip4_addr(netif))) {
+ /* fill in object properties */
+ return ip_AddrTable_get_cell_value_core(netif, column, value, value_len);
+ }
+
+ netif = netif->next;
+ }
+
+ /* not found */
+ return SNMP_ERR_NOSUCHINSTANCE;
+}
+
+static snmp_err_t
+ip_AddrTable_get_next_cell_instance_and_value(const u32_t* column, struct snmp_obj_id* row_oid, union snmp_variant_value* value, u32_t* value_len)
+{
+ struct netif *netif;
+ struct snmp_next_oid_state state;
+ u32_t result_temp[LWIP_ARRAYSIZE(ip_AddrTable_oid_ranges)];
+
+ /* init struct to search next oid */
+ snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(ip_AddrTable_oid_ranges));
+
+ /* iterate over all possible OIDs to find the next one */
+ netif = netif_list;
+ while (netif != NULL) {
+ u32_t test_oid[LWIP_ARRAYSIZE(ip_AddrTable_oid_ranges)];
+ snmp_ip4_to_oid(netif_ip4_addr(netif), &test_oid[0]);
+
+ /* check generated OID: is it a candidate for the next one? */
+ snmp_next_oid_check(&state, test_oid, LWIP_ARRAYSIZE(ip_AddrTable_oid_ranges), netif);
+
+ netif = netif->next;
+ }
+
+ /* did we find a next one? */
+ if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) {
+ snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len);
+ /* fill in object properties */
+ return ip_AddrTable_get_cell_value_core((struct netif*)state.reference, column, value, value_len);
+ }
+
+ /* not found */
+ return SNMP_ERR_NOSUCHINSTANCE;
+}
+
+/* --- ipRouteTable --- */
+
+/* list of allowed value ranges for incoming OID */
+static const struct snmp_oid_range ip_RouteTable_oid_ranges[] = {
+ { 0, 0xff }, /* IP A */
+ { 0, 0xff }, /* IP B */
+ { 0, 0xff }, /* IP C */
+ { 0, 0xff }, /* IP D */
+};
+
+static snmp_err_t
+ip_RouteTable_get_cell_value_core(struct netif *netif, u8_t default_route, const u32_t* column, union snmp_variant_value* value, u32_t* value_len)
+{
+ switch (*column) {
+ case 1: /* ipRouteDest */
+ if (default_route) {
+ /* default rte has 0.0.0.0 dest */
+ value->u32 = IP4_ADDR_ANY4->addr;
+ } else {
+ /* netifs have netaddress dest */
+ ip4_addr_t tmp;
+ ip4_addr_get_network(&tmp, netif_ip4_addr(netif), netif_ip4_netmask(netif));
+ value->u32 = tmp.addr;
+ }
+ break;
+ case 2: /* ipRouteIfIndex */
+ value->u32 = netif_to_num(netif);
+ break;
+ case 3: /* ipRouteMetric1 */
+ if (default_route) {
+ value->s32 = 1; /* default */
+ } else {
+ value->s32 = 0; /* normal */
+ }
+ break;
+ case 4: /* ipRouteMetric2 */
+ case 5: /* ipRouteMetric3 */
+ case 6: /* ipRouteMetric4 */
+ value->s32 = -1; /* none */
+ break;
+ case 7: /* ipRouteNextHop */
+ if (default_route) {
+ /* default rte: gateway */
+ value->u32 = netif_ip4_gw(netif)->addr;
+ } else {
+ /* other rtes: netif ip_addr */
+ value->u32 = netif_ip4_addr(netif)->addr;
+ }
+ break;
+ case 8: /* ipRouteType */
+ if (default_route) {
+ /* default rte is indirect */
+ value->u32 = 4; /* indirect */
+ } else {
+ /* other rtes are direct */
+ value->u32 = 3; /* direct */
+ }
+ break;
+ case 9: /* ipRouteProto */
+ /* locally defined routes */
+ value->u32 = 2; /* local */
+ break;
+ case 10: /* ipRouteAge */
+ /* @todo (sysuptime - timestamp last change) / 100 */
+ value->u32 = 0;
+ break;
+ case 11: /* ipRouteMask */
+ if (default_route) {
+ /* default rte use 0.0.0.0 mask */
+ value->u32 = IP4_ADDR_ANY4->addr;
+ } else {
+ /* other rtes use netmask */
+ value->u32 = netif_ip4_netmask(netif)->addr;
+ }
+ break;
+ case 12: /* ipRouteMetric5 */
+ value->s32 = -1; /* none */
+ break;
+ case 13: /* ipRouteInfo */
+ value->const_ptr = snmp_zero_dot_zero.id;
+ *value_len = snmp_zero_dot_zero.len * sizeof(u32_t);
+ break;
+ default:
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ return SNMP_ERR_NOERROR;
+}
+
+static snmp_err_t
+ip_RouteTable_get_cell_value(const u32_t* column, const u32_t* row_oid, u8_t row_oid_len, union snmp_variant_value* value, u32_t* value_len)
+{
+ ip4_addr_t test_ip;
+ struct netif *netif;
+
+ /* check if incoming OID length and if values are in plausible range */
+ if (!snmp_oid_in_range(row_oid, row_oid_len, ip_RouteTable_oid_ranges, LWIP_ARRAYSIZE(ip_RouteTable_oid_ranges))) {
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ /* get IP and port from incoming OID */
+ snmp_oid_to_ip4(&row_oid[0], &test_ip); /* we know it succeeds because of oid_in_range check above */
+
+ /* default route is on default netif */
+ if (ip4_addr_isany_val(test_ip) && (netif_default != NULL)) {
+ /* fill in object properties */
+ return ip_RouteTable_get_cell_value_core(netif_default, 1, column, value, value_len);
+ }
+
+ /* find netif with requested route */
+ netif = netif_list;
+ while (netif != NULL) {
+ ip4_addr_t dst;
+ ip4_addr_get_network(&dst, netif_ip4_addr(netif), netif_ip4_netmask(netif));
+
+ if (ip4_addr_cmp(&dst, &test_ip)) {
+ /* fill in object properties */
+ return ip_RouteTable_get_cell_value_core(netif, 0, column, value, value_len);
+ }
+
+ netif = netif->next;
+ }
+
+ /* not found */
+ return SNMP_ERR_NOSUCHINSTANCE;
+}
+
+static snmp_err_t
+ip_RouteTable_get_next_cell_instance_and_value(const u32_t* column, struct snmp_obj_id* row_oid, union snmp_variant_value* value, u32_t* value_len)
+{
+ struct netif *netif;
+ struct snmp_next_oid_state state;
+ u32_t result_temp[LWIP_ARRAYSIZE(ip_RouteTable_oid_ranges)];
+ u32_t test_oid[LWIP_ARRAYSIZE(ip_RouteTable_oid_ranges)];
+
+ /* init struct to search next oid */
+ snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(ip_RouteTable_oid_ranges));
+
+ /* check default route */
+ if (netif_default != NULL) {
+ snmp_ip4_to_oid(IP4_ADDR_ANY4, &test_oid[0]);
+ snmp_next_oid_check(&state, test_oid, LWIP_ARRAYSIZE(ip_RouteTable_oid_ranges), netif_default);
+ }
+
+ /* iterate over all possible OIDs to find the next one */
+ netif = netif_list;
+ while (netif != NULL) {
+ ip4_addr_t dst;
+ ip4_addr_get_network(&dst, netif_ip4_addr(netif), netif_ip4_netmask(netif));
+
+ /* check generated OID: is it a candidate for the next one? */
+ if (!ip4_addr_isany_val(dst)) {
+ snmp_ip4_to_oid(&dst, &test_oid[0]);
+ snmp_next_oid_check(&state, test_oid, LWIP_ARRAYSIZE(ip_RouteTable_oid_ranges), netif);
+ }
+
+ netif = netif->next;
+ }
+
+ /* did we find a next one? */
+ if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) {
+ ip4_addr_t dst;
+ snmp_oid_to_ip4(&result_temp[0], &dst);
+ snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len);
+ /* fill in object properties */
+ return ip_RouteTable_get_cell_value_core((struct netif*)state.reference, ip4_addr_isany_val(dst), column, value, value_len);
+ } else {
+ /* not found */
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+}
+
+#if LWIP_ARP && LWIP_IPV4
+/* --- ipNetToMediaTable --- */
+
+/* list of allowed value ranges for incoming OID */
+static const struct snmp_oid_range ip_NetToMediaTable_oid_ranges[] = {
+ { 1, 0xff }, /* IfIndex */
+ { 0, 0xff }, /* IP A */
+ { 0, 0xff }, /* IP B */
+ { 0, 0xff }, /* IP C */
+ { 0, 0xff } /* IP D */
+};
+
+static snmp_err_t
+ip_NetToMediaTable_get_cell_value_core(u8_t arp_table_index, const u32_t* column, union snmp_variant_value* value, u32_t* value_len)
+{
+ ip4_addr_t *ip;
+ struct netif *netif;
+ struct eth_addr *ethaddr;
+
+ etharp_get_entry(arp_table_index, &ip, &netif, &ethaddr);
+
+ /* value */
+ switch (*column) {
+ case 1: /* atIfIndex / ipNetToMediaIfIndex */
+ value->u32 = netif_to_num(netif);
+ break;
+ case 2: /* atPhysAddress / ipNetToMediaPhysAddress */
+ value->ptr = ethaddr;
+ *value_len = sizeof(*ethaddr);
+ break;
+ case 3: /* atNetAddress / ipNetToMediaNetAddress */
+ value->u32 = ip->addr;
+ break;
+ case 4: /* ipNetToMediaType */
+ value->u32 = 3; /* dynamic*/
+ break;
+ default:
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ return SNMP_ERR_NOERROR;
+}
+
+static snmp_err_t
+ip_NetToMediaTable_get_cell_value(const u32_t* column, const u32_t* row_oid, u8_t row_oid_len, union snmp_variant_value* value, u32_t* value_len)
+{
+ ip4_addr_t ip_in;
+ u8_t netif_index;
+ u8_t i;
+
+ /* check if incoming OID length and if values are in plausible range */
+ if (!snmp_oid_in_range(row_oid, row_oid_len, ip_NetToMediaTable_oid_ranges, LWIP_ARRAYSIZE(ip_NetToMediaTable_oid_ranges))) {
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ /* get IP from incoming OID */
+ netif_index = (u8_t)row_oid[0];
+ snmp_oid_to_ip4(&row_oid[1], &ip_in); /* we know it succeeds because of oid_in_range check above */
+
+ /* find requested entry */
+ for (i=0; i<ARP_TABLE_SIZE; i++) {
+ ip4_addr_t *ip;
+ struct netif *netif;
+ struct eth_addr *ethaddr;
+
+ if (etharp_get_entry(i, &ip, &netif, &ethaddr)) {
+ if ((netif_index == netif_to_num(netif)) && ip4_addr_cmp(&ip_in, ip)) {
+ /* fill in object properties */
+ return ip_NetToMediaTable_get_cell_value_core(i, column, value, value_len);
+ }
+ }
+ }
+
+ /* not found */
+ return SNMP_ERR_NOSUCHINSTANCE;
+}
+
+static snmp_err_t
+ip_NetToMediaTable_get_next_cell_instance_and_value(const u32_t* column, struct snmp_obj_id* row_oid, union snmp_variant_value* value, u32_t* value_len)
+{
+ u8_t i;
+ struct snmp_next_oid_state state;
+ u32_t result_temp[LWIP_ARRAYSIZE(ip_NetToMediaTable_oid_ranges)];
+
+ /* init struct to search next oid */
+ snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(ip_NetToMediaTable_oid_ranges));
+
+ /* iterate over all possible OIDs to find the next one */
+ for (i=0; i<ARP_TABLE_SIZE; i++) {
+ ip4_addr_t *ip;
+ struct netif *netif;
+ struct eth_addr *ethaddr;
+
+ if (etharp_get_entry(i, &ip, &netif, &ethaddr)) {
+ u32_t test_oid[LWIP_ARRAYSIZE(ip_NetToMediaTable_oid_ranges)];
+
+ test_oid[0] = netif_to_num(netif);
+ snmp_ip4_to_oid(ip, &test_oid[1]);
+
+ /* check generated OID: is it a candidate for the next one? */
+ snmp_next_oid_check(&state, test_oid, LWIP_ARRAYSIZE(ip_NetToMediaTable_oid_ranges), LWIP_PTR_NUMERIC_CAST(void*, i));
+ }
+ }
+
+ /* did we find a next one? */
+ if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) {
+ snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len);
+ /* fill in object properties */
+ return ip_NetToMediaTable_get_cell_value_core(LWIP_PTR_NUMERIC_CAST(u8_t, state.reference), column, value, value_len);
+ }
+
+ /* not found */
+ return SNMP_ERR_NOSUCHINSTANCE;
+}
+
+#endif /* LWIP_ARP && LWIP_IPV4 */
+
+static const struct snmp_scalar_node ip_Forwarding = SNMP_SCALAR_CREATE_NODE(1, SNMP_NODE_INSTANCE_READ_WRITE, SNMP_ASN1_TYPE_INTEGER, ip_get_value, ip_set_test, ip_set_value);
+static const struct snmp_scalar_node ip_DefaultTTL = SNMP_SCALAR_CREATE_NODE(2, SNMP_NODE_INSTANCE_READ_WRITE, SNMP_ASN1_TYPE_INTEGER, ip_get_value, ip_set_test, ip_set_value);
+static const struct snmp_scalar_node ip_InReceives = SNMP_SCALAR_CREATE_NODE_READONLY(3, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
+static const struct snmp_scalar_node ip_InHdrErrors = SNMP_SCALAR_CREATE_NODE_READONLY(4, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
+static const struct snmp_scalar_node ip_InAddrErrors = SNMP_SCALAR_CREATE_NODE_READONLY(5, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
+static const struct snmp_scalar_node ip_ForwDatagrams = SNMP_SCALAR_CREATE_NODE_READONLY(6, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
+static const struct snmp_scalar_node ip_InUnknownProtos = SNMP_SCALAR_CREATE_NODE_READONLY(7, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
+static const struct snmp_scalar_node ip_InDiscards = SNMP_SCALAR_CREATE_NODE_READONLY(8, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
+static const struct snmp_scalar_node ip_InDelivers = SNMP_SCALAR_CREATE_NODE_READONLY(9, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
+static const struct snmp_scalar_node ip_OutRequests = SNMP_SCALAR_CREATE_NODE_READONLY(10, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
+static const struct snmp_scalar_node ip_OutDiscards = SNMP_SCALAR_CREATE_NODE_READONLY(11, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
+static const struct snmp_scalar_node ip_OutNoRoutes = SNMP_SCALAR_CREATE_NODE_READONLY(12, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
+static const struct snmp_scalar_node ip_ReasmTimeout = SNMP_SCALAR_CREATE_NODE_READONLY(13, SNMP_ASN1_TYPE_INTEGER, ip_get_value);
+static const struct snmp_scalar_node ip_ReasmReqds = SNMP_SCALAR_CREATE_NODE_READONLY(14, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
+static const struct snmp_scalar_node ip_ReasmOKs = SNMP_SCALAR_CREATE_NODE_READONLY(15, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
+static const struct snmp_scalar_node ip_ReasmFails = SNMP_SCALAR_CREATE_NODE_READONLY(16, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
+static const struct snmp_scalar_node ip_FragOKs = SNMP_SCALAR_CREATE_NODE_READONLY(17, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
+static const struct snmp_scalar_node ip_FragFails = SNMP_SCALAR_CREATE_NODE_READONLY(18, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
+static const struct snmp_scalar_node ip_FragCreates = SNMP_SCALAR_CREATE_NODE_READONLY(19, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
+static const struct snmp_scalar_node ip_RoutingDiscards = SNMP_SCALAR_CREATE_NODE_READONLY(23, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
+
+static const struct snmp_table_simple_col_def ip_AddrTable_columns[] = {
+ { 1, SNMP_ASN1_TYPE_IPADDR, SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipAdEntAddr */
+ { 2, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipAdEntIfIndex */
+ { 3, SNMP_ASN1_TYPE_IPADDR, SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipAdEntNetMask */
+ { 4, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipAdEntBcastAddr */
+ { 5, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 } /* ipAdEntReasmMaxSize */
+};
+
+static const struct snmp_table_simple_node ip_AddrTable = SNMP_TABLE_CREATE_SIMPLE(20, ip_AddrTable_columns, ip_AddrTable_get_cell_value, ip_AddrTable_get_next_cell_instance_and_value);
+
+static const struct snmp_table_simple_col_def ip_RouteTable_columns[] = {
+ { 1, SNMP_ASN1_TYPE_IPADDR, SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipRouteDest */
+ { 2, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipRouteIfIndex */
+ { 3, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_S32 }, /* ipRouteMetric1 */
+ { 4, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_S32 }, /* ipRouteMetric2 */
+ { 5, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_S32 }, /* ipRouteMetric3 */
+ { 6, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_S32 }, /* ipRouteMetric4 */
+ { 7, SNMP_ASN1_TYPE_IPADDR, SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipRouteNextHop */
+ { 8, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipRouteType */
+ { 9, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipRouteProto */
+ { 10, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipRouteAge */
+ { 11, SNMP_ASN1_TYPE_IPADDR, SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipRouteMask */
+ { 12, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_S32 }, /* ipRouteMetric5 */
+ { 13, SNMP_ASN1_TYPE_OBJECT_ID, SNMP_VARIANT_VALUE_TYPE_PTR } /* ipRouteInfo */
+};
+
+static const struct snmp_table_simple_node ip_RouteTable = SNMP_TABLE_CREATE_SIMPLE(21, ip_RouteTable_columns, ip_RouteTable_get_cell_value, ip_RouteTable_get_next_cell_instance_and_value);
+#endif /* LWIP_IPV4 */
+
+#if LWIP_ARP && LWIP_IPV4
+static const struct snmp_table_simple_col_def ip_NetToMediaTable_columns[] = {
+ { 1, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipNetToMediaIfIndex */
+ { 2, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_VARIANT_VALUE_TYPE_PTR }, /* ipNetToMediaPhysAddress */
+ { 3, SNMP_ASN1_TYPE_IPADDR, SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipNetToMediaNetAddress */
+ { 4, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 } /* ipNetToMediaType */
+};
+
+static const struct snmp_table_simple_node ip_NetToMediaTable = SNMP_TABLE_CREATE_SIMPLE(22, ip_NetToMediaTable_columns, ip_NetToMediaTable_get_cell_value, ip_NetToMediaTable_get_next_cell_instance_and_value);
+#endif /* LWIP_ARP && LWIP_IPV4 */
+
+#if LWIP_IPV4
+/* the following nodes access variables in LWIP stack from SNMP worker thread and must therefore be synced to LWIP (TCPIP) thread */
+CREATE_LWIP_SYNC_NODE( 1, ip_Forwarding)
+CREATE_LWIP_SYNC_NODE( 2, ip_DefaultTTL)
+CREATE_LWIP_SYNC_NODE( 3, ip_InReceives)
+CREATE_LWIP_SYNC_NODE( 4, ip_InHdrErrors)
+CREATE_LWIP_SYNC_NODE( 5, ip_InAddrErrors)
+CREATE_LWIP_SYNC_NODE( 6, ip_ForwDatagrams)
+CREATE_LWIP_SYNC_NODE( 7, ip_InUnknownProtos)
+CREATE_LWIP_SYNC_NODE( 8, ip_InDiscards)
+CREATE_LWIP_SYNC_NODE( 9, ip_InDelivers)
+CREATE_LWIP_SYNC_NODE(10, ip_OutRequests)
+CREATE_LWIP_SYNC_NODE(11, ip_OutDiscards)
+CREATE_LWIP_SYNC_NODE(12, ip_OutNoRoutes)
+CREATE_LWIP_SYNC_NODE(13, ip_ReasmTimeout)
+CREATE_LWIP_SYNC_NODE(14, ip_ReasmReqds)
+CREATE_LWIP_SYNC_NODE(15, ip_ReasmOKs)
+CREATE_LWIP_SYNC_NODE(15, ip_ReasmFails)
+CREATE_LWIP_SYNC_NODE(17, ip_FragOKs)
+CREATE_LWIP_SYNC_NODE(18, ip_FragFails)
+CREATE_LWIP_SYNC_NODE(19, ip_FragCreates)
+CREATE_LWIP_SYNC_NODE(20, ip_AddrTable)
+CREATE_LWIP_SYNC_NODE(21, ip_RouteTable)
+#if LWIP_ARP
+CREATE_LWIP_SYNC_NODE(22, ip_NetToMediaTable)
+#endif /* LWIP_ARP */
+CREATE_LWIP_SYNC_NODE(23, ip_RoutingDiscards)
+
+static const struct snmp_node* const ip_nodes[] = {
+ &SYNC_NODE_NAME(ip_Forwarding).node.node,
+ &SYNC_NODE_NAME(ip_DefaultTTL).node.node,
+ &SYNC_NODE_NAME(ip_InReceives).node.node,
+ &SYNC_NODE_NAME(ip_InHdrErrors).node.node,
+ &SYNC_NODE_NAME(ip_InAddrErrors).node.node,
+ &SYNC_NODE_NAME(ip_ForwDatagrams).node.node,
+ &SYNC_NODE_NAME(ip_InUnknownProtos).node.node,
+ &SYNC_NODE_NAME(ip_InDiscards).node.node,
+ &SYNC_NODE_NAME(ip_InDelivers).node.node,
+ &SYNC_NODE_NAME(ip_OutRequests).node.node,
+ &SYNC_NODE_NAME(ip_OutDiscards).node.node,
+ &SYNC_NODE_NAME(ip_OutNoRoutes).node.node,
+ &SYNC_NODE_NAME(ip_ReasmTimeout).node.node,
+ &SYNC_NODE_NAME(ip_ReasmReqds).node.node,
+ &SYNC_NODE_NAME(ip_ReasmOKs).node.node,
+ &SYNC_NODE_NAME(ip_ReasmFails).node.node,
+ &SYNC_NODE_NAME(ip_FragOKs).node.node,
+ &SYNC_NODE_NAME(ip_FragFails).node.node,
+ &SYNC_NODE_NAME(ip_FragCreates).node.node,
+ &SYNC_NODE_NAME(ip_AddrTable).node.node,
+ &SYNC_NODE_NAME(ip_RouteTable).node.node,
+#if LWIP_ARP
+ &SYNC_NODE_NAME(ip_NetToMediaTable).node.node,
+#endif /* LWIP_ARP */
+ &SYNC_NODE_NAME(ip_RoutingDiscards).node.node
+};
+
+const struct snmp_tree_node snmp_mib2_ip_root = SNMP_CREATE_TREE_NODE(4, ip_nodes);
+#endif /* LWIP_IPV4 */
+
+/* --- at .1.3.6.1.2.1.3 ----------------------------------------------------- */
+
+#if LWIP_ARP && LWIP_IPV4
+/* at node table is a subset of ip_nettomedia table (same rows but less columns) */
+static const struct snmp_table_simple_col_def at_Table_columns[] = {
+ { 1, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 }, /* atIfIndex */
+ { 2, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_VARIANT_VALUE_TYPE_PTR }, /* atPhysAddress */
+ { 3, SNMP_ASN1_TYPE_IPADDR, SNMP_VARIANT_VALUE_TYPE_U32 } /* atNetAddress */
+};
+
+static const struct snmp_table_simple_node at_Table = SNMP_TABLE_CREATE_SIMPLE(1, at_Table_columns, ip_NetToMediaTable_get_cell_value, ip_NetToMediaTable_get_next_cell_instance_and_value);
+
+/* the following nodes access variables in LWIP stack from SNMP worker thread and must therefore be synced to LWIP (TCPIP) thread */
+CREATE_LWIP_SYNC_NODE(1, at_Table)
+
+static const struct snmp_node* const at_nodes[] = {
+ &SYNC_NODE_NAME(at_Table).node.node
+};
+
+const struct snmp_tree_node snmp_mib2_at_root = SNMP_CREATE_TREE_NODE(3, at_nodes);
+#endif /* LWIP_ARP && LWIP_IPV4 */
+
+#endif /* LWIP_SNMP && SNMP_LWIP_MIB2 */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_mib2_snmp.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_mib2_snmp.c
new file mode 100644
index 0000000..8a36d61
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_mib2_snmp.c
@@ -0,0 +1,227 @@
+/**
+ * @file
+ * Management Information Base II (RFC1213) SNMP objects and 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: Dirk Ziegelmeier <dziegel@gmx.de>
+ * Christiaan Simons <christiaan.simons@axon.tv>
+ */
+
+#include "lwip/snmp.h"
+#include "lwip/apps/snmp.h"
+#include "lwip/apps/snmp_core.h"
+#include "lwip/apps/snmp_mib2.h"
+#include "lwip/apps/snmp_scalar.h"
+
+#if LWIP_SNMP && SNMP_LWIP_MIB2
+
+#define MIB2_AUTH_TRAPS_ENABLED 1
+#define MIB2_AUTH_TRAPS_DISABLED 2
+
+/* --- snmp .1.3.6.1.2.1.11 ----------------------------------------------------- */
+static s16_t
+snmp_get_value(const struct snmp_scalar_array_node_def *node, void *value)
+{
+ u32_t *uint_ptr = (u32_t*)value;
+ switch (node->oid) {
+ case 1: /* snmpInPkts */
+ *uint_ptr = snmp_stats.inpkts;
+ break;
+ case 2: /* snmpOutPkts */
+ *uint_ptr = snmp_stats.outpkts;
+ break;
+ case 3: /* snmpInBadVersions */
+ *uint_ptr = snmp_stats.inbadversions;
+ break;
+ case 4: /* snmpInBadCommunityNames */
+ *uint_ptr = snmp_stats.inbadcommunitynames;
+ break;
+ case 5: /* snmpInBadCommunityUses */
+ *uint_ptr = snmp_stats.inbadcommunityuses;
+ break;
+ case 6: /* snmpInASNParseErrs */
+ *uint_ptr = snmp_stats.inasnparseerrs;
+ break;
+ case 8: /* snmpInTooBigs */
+ *uint_ptr = snmp_stats.intoobigs;
+ break;
+ case 9: /* snmpInNoSuchNames */
+ *uint_ptr = snmp_stats.innosuchnames;
+ break;
+ case 10: /* snmpInBadValues */
+ *uint_ptr = snmp_stats.inbadvalues;
+ break;
+ case 11: /* snmpInReadOnlys */
+ *uint_ptr = snmp_stats.inreadonlys;
+ break;
+ case 12: /* snmpInGenErrs */
+ *uint_ptr = snmp_stats.ingenerrs;
+ break;
+ case 13: /* snmpInTotalReqVars */
+ *uint_ptr = snmp_stats.intotalreqvars;
+ break;
+ case 14: /* snmpInTotalSetVars */
+ *uint_ptr = snmp_stats.intotalsetvars;
+ break;
+ case 15: /* snmpInGetRequests */
+ *uint_ptr = snmp_stats.ingetrequests;
+ break;
+ case 16: /* snmpInGetNexts */
+ *uint_ptr = snmp_stats.ingetnexts;
+ break;
+ case 17: /* snmpInSetRequests */
+ *uint_ptr = snmp_stats.insetrequests;
+ break;
+ case 18: /* snmpInGetResponses */
+ *uint_ptr = snmp_stats.ingetresponses;
+ break;
+ case 19: /* snmpInTraps */
+ *uint_ptr = snmp_stats.intraps;
+ break;
+ case 20: /* snmpOutTooBigs */
+ *uint_ptr = snmp_stats.outtoobigs;
+ break;
+ case 21: /* snmpOutNoSuchNames */
+ *uint_ptr = snmp_stats.outnosuchnames;
+ break;
+ case 22: /* snmpOutBadValues */
+ *uint_ptr = snmp_stats.outbadvalues;
+ break;
+ case 24: /* snmpOutGenErrs */
+ *uint_ptr = snmp_stats.outgenerrs;
+ break;
+ case 25: /* snmpOutGetRequests */
+ *uint_ptr = snmp_stats.outgetrequests;
+ break;
+ case 26: /* snmpOutGetNexts */
+ *uint_ptr = snmp_stats.outgetnexts;
+ break;
+ case 27: /* snmpOutSetRequests */
+ *uint_ptr = snmp_stats.outsetrequests;
+ break;
+ case 28: /* snmpOutGetResponses */
+ *uint_ptr = snmp_stats.outgetresponses;
+ break;
+ case 29: /* snmpOutTraps */
+ *uint_ptr = snmp_stats.outtraps;
+ break;
+ case 30: /* snmpEnableAuthenTraps */
+ if (snmp_get_auth_traps_enabled() == SNMP_AUTH_TRAPS_DISABLED) {
+ *uint_ptr = MIB2_AUTH_TRAPS_DISABLED;
+ } else {
+ *uint_ptr = MIB2_AUTH_TRAPS_ENABLED;
+ }
+ break;
+ case 31: /* snmpSilentDrops */
+ *uint_ptr = 0; /* not supported */
+ break;
+ case 32: /* snmpProxyDrops */
+ *uint_ptr = 0; /* not supported */
+ break;
+ default:
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmp_get_value(): unknown id: %"S32_F"\n", node->oid));
+ return 0;
+ }
+
+ return sizeof(*uint_ptr);
+}
+
+static snmp_err_t
+snmp_set_test(const struct snmp_scalar_array_node_def *node, u16_t len, void *value)
+{
+ snmp_err_t ret = SNMP_ERR_WRONGVALUE;
+ LWIP_UNUSED_ARG(len);
+
+ if (node->oid == 30) {
+ /* snmpEnableAuthenTraps */
+ s32_t *sint_ptr = (s32_t*)value;
+
+ /* we should have writable non-volatile mem here */
+ if ((*sint_ptr == MIB2_AUTH_TRAPS_DISABLED) || (*sint_ptr == MIB2_AUTH_TRAPS_ENABLED)) {
+ ret = SNMP_ERR_NOERROR;
+ }
+ }
+ return ret;
+}
+
+static snmp_err_t
+snmp_set_value(const struct snmp_scalar_array_node_def *node, u16_t len, void *value)
+{
+ LWIP_UNUSED_ARG(len);
+
+ if (node->oid == 30) {
+ /* snmpEnableAuthenTraps */
+ s32_t *sint_ptr = (s32_t*)value;
+ if (*sint_ptr == MIB2_AUTH_TRAPS_DISABLED) {
+ snmp_set_auth_traps_enabled(SNMP_AUTH_TRAPS_DISABLED);
+ } else {
+ snmp_set_auth_traps_enabled(SNMP_AUTH_TRAPS_ENABLED);
+ }
+ }
+
+ return SNMP_ERR_NOERROR;
+}
+
+/* the following nodes access variables in SNMP stack (snmp_stats) from SNMP worker thread -> OK, no sync needed */
+static const struct snmp_scalar_array_node_def snmp_nodes[] = {
+ { 1, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInPkts */
+ { 2, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpOutPkts */
+ { 3, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInBadVersions */
+ { 4, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInBadCommunityNames */
+ { 5, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInBadCommunityUses */
+ { 6, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInASNParseErrs */
+ { 8, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInTooBigs */
+ { 9, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInNoSuchNames */
+ {10, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInBadValues */
+ {11, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInReadOnlys */
+ {12, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInGenErrs */
+ {13, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInTotalReqVars */
+ {14, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInTotalSetVars */
+ {15, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInGetRequests */
+ {16, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInGetNexts */
+ {17, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInSetRequests */
+ {18, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInGetResponses */
+ {19, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInTraps */
+ {20, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpOutTooBigs */
+ {21, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpOutNoSuchNames */
+ {22, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpOutBadValues */
+ {24, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpOutGenErrs */
+ {25, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpOutGetRequests */
+ {26, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpOutGetNexts */
+ {27, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpOutSetRequests */
+ {28, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpOutGetResponses */
+ {29, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpOutTraps */
+ {30, SNMP_ASN1_TYPE_INTEGER, SNMP_NODE_INSTANCE_READ_WRITE}, /* snmpEnableAuthenTraps */
+ {31, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpSilentDrops */
+ {32, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY} /* snmpProxyDrops */
+};
+
+const struct snmp_scalar_array_node snmp_mib2_snmp_root = SNMP_SCALAR_CREATE_ARRAY_NODE(11, snmp_nodes, snmp_get_value, snmp_set_test, snmp_set_value);
+
+#endif /* LWIP_SNMP && SNMP_LWIP_MIB2 */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_mib2_system.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_mib2_system.c
new file mode 100644
index 0000000..90e5780
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_mib2_system.c
@@ -0,0 +1,377 @@
+/**
+ * @file
+ * Management Information Base II (RFC1213) SYSTEM objects and 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: Dirk Ziegelmeier <dziegel@gmx.de>
+ * Christiaan Simons <christiaan.simons@axon.tv>
+ */
+
+#include "lwip/snmp.h"
+#include "lwip/apps/snmp.h"
+#include "lwip/apps/snmp_core.h"
+#include "lwip/apps/snmp_mib2.h"
+#include "lwip/apps/snmp_table.h"
+#include "lwip/apps/snmp_scalar.h"
+#include "lwip/sys.h"
+
+#include <string.h>
+
+#if LWIP_SNMP && SNMP_LWIP_MIB2
+
+#if SNMP_USE_NETCONN
+#define SYNC_NODE_NAME(node_name) node_name ## _synced
+#define CREATE_LWIP_SYNC_NODE(oid, node_name) \
+ static const struct snmp_threadsync_node node_name ## _synced = SNMP_CREATE_THREAD_SYNC_NODE(oid, &node_name.node, &snmp_mib2_lwip_locks);
+#else
+#define SYNC_NODE_NAME(node_name) node_name
+#define CREATE_LWIP_SYNC_NODE(oid, node_name)
+#endif
+
+/* --- system .1.3.6.1.2.1.1 ----------------------------------------------------- */
+
+/** mib-2.system.sysDescr */
+static const u8_t sysdescr_default[] = SNMP_LWIP_MIB2_SYSDESC;
+static const u8_t* sysdescr = sysdescr_default;
+static const u16_t* sysdescr_len = NULL; /* use strlen for determining len */
+
+/** mib-2.system.sysContact */
+static const u8_t syscontact_default[] = SNMP_LWIP_MIB2_SYSCONTACT;
+static const u8_t* syscontact = syscontact_default;
+static const u16_t* syscontact_len = NULL; /* use strlen for determining len */
+static u8_t* syscontact_wr = NULL; /* if writable, points to the same buffer as syscontact (required for correct constness) */
+static u16_t* syscontact_wr_len = NULL; /* if writable, points to the same buffer as syscontact_len (required for correct constness) */
+static u16_t syscontact_bufsize = 0; /* 0=not writable */
+
+/** mib-2.system.sysName */
+static const u8_t sysname_default[] = SNMP_LWIP_MIB2_SYSNAME;
+static const u8_t* sysname = sysname_default;
+static const u16_t* sysname_len = NULL; /* use strlen for determining len */
+static u8_t* sysname_wr = NULL; /* if writable, points to the same buffer as sysname (required for correct constness) */
+static u16_t* sysname_wr_len = NULL; /* if writable, points to the same buffer as sysname_len (required for correct constness) */
+static u16_t sysname_bufsize = 0; /* 0=not writable */
+
+/** mib-2.system.sysLocation */
+static const u8_t syslocation_default[] = SNMP_LWIP_MIB2_SYSLOCATION;
+static const u8_t* syslocation = syslocation_default;
+static const u16_t* syslocation_len = NULL; /* use strlen for determining len */
+static u8_t* syslocation_wr = NULL; /* if writable, points to the same buffer as syslocation (required for correct constness) */
+static u16_t* syslocation_wr_len = NULL; /* if writable, points to the same buffer as syslocation_len (required for correct constness) */
+static u16_t syslocation_bufsize = 0; /* 0=not writable */
+
+/**
+ * @ingroup snmp_mib2
+ * Initializes sysDescr pointers.
+ *
+ * @param str if non-NULL then copy str pointer
+ * @param len points to string length, excluding zero terminator
+ */
+void
+snmp_mib2_set_sysdescr(const u8_t *str, const u16_t *len)
+{
+ if (str != NULL) {
+ sysdescr = str;
+ sysdescr_len = len;
+ }
+}
+
+/**
+ * @ingroup snmp_mib2
+ * Initializes sysContact pointers
+ *
+ * @param ocstr if non-NULL then copy str pointer
+ * @param ocstrlen points to string length, excluding zero terminator.
+ * if set to NULL it is assumed that ocstr is NULL-terminated.
+ * @param bufsize size of the buffer in bytes.
+ * (this is required because the buffer can be overwritten by snmp-set)
+ * if ocstrlen is NULL buffer needs space for terminating 0 byte.
+ * otherwise complete buffer is used for string.
+ * if bufsize is set to 0, the value is regarded as read-only.
+ */
+void
+snmp_mib2_set_syscontact(u8_t *ocstr, u16_t *ocstrlen, u16_t bufsize)
+{
+ if (ocstr != NULL) {
+ syscontact = ocstr;
+ syscontact_wr = ocstr;
+ syscontact_len = ocstrlen;
+ syscontact_wr_len = ocstrlen;
+ syscontact_bufsize = bufsize;
+ }
+}
+
+/**
+ * @ingroup snmp_mib2
+ * see \ref snmp_mib2_set_syscontact but set pointer to readonly memory
+ */
+void
+snmp_mib2_set_syscontact_readonly(const u8_t *ocstr, const u16_t *ocstrlen)
+{
+ if (ocstr != NULL) {
+ syscontact = ocstr;
+ syscontact_len = ocstrlen;
+ syscontact_wr = NULL;
+ syscontact_wr_len = NULL;
+ syscontact_bufsize = 0;
+ }
+}
+
+
+/**
+ * @ingroup snmp_mib2
+ * Initializes sysName pointers
+ *
+ * @param ocstr if non-NULL then copy str pointer
+ * @param ocstrlen points to string length, excluding zero terminator.
+ * if set to NULL it is assumed that ocstr is NULL-terminated.
+ * @param bufsize size of the buffer in bytes.
+ * (this is required because the buffer can be overwritten by snmp-set)
+ * if ocstrlen is NULL buffer needs space for terminating 0 byte.
+ * otherwise complete buffer is used for string.
+ * if bufsize is set to 0, the value is regarded as read-only.
+ */
+void
+snmp_mib2_set_sysname(u8_t *ocstr, u16_t *ocstrlen, u16_t bufsize)
+{
+ if (ocstr != NULL) {
+ sysname = ocstr;
+ sysname_wr = ocstr;
+ sysname_len = ocstrlen;
+ sysname_wr_len = ocstrlen;
+ sysname_bufsize = bufsize;
+ }
+}
+
+/**
+ * @ingroup snmp_mib2
+ * see \ref snmp_mib2_set_sysname but set pointer to readonly memory
+ */
+void
+snmp_mib2_set_sysname_readonly(const u8_t *ocstr, const u16_t *ocstrlen)
+{
+ if (ocstr != NULL) {
+ sysname = ocstr;
+ sysname_len = ocstrlen;
+ sysname_wr = NULL;
+ sysname_wr_len = NULL;
+ sysname_bufsize = 0;
+ }
+}
+
+/**
+ * @ingroup snmp_mib2
+ * Initializes sysLocation pointers
+ *
+ * @param ocstr if non-NULL then copy str pointer
+ * @param ocstrlen points to string length, excluding zero terminator.
+ * if set to NULL it is assumed that ocstr is NULL-terminated.
+ * @param bufsize size of the buffer in bytes.
+ * (this is required because the buffer can be overwritten by snmp-set)
+ * if ocstrlen is NULL buffer needs space for terminating 0 byte.
+ * otherwise complete buffer is used for string.
+ * if bufsize is set to 0, the value is regarded as read-only.
+ */
+void
+snmp_mib2_set_syslocation(u8_t *ocstr, u16_t *ocstrlen, u16_t bufsize)
+{
+ if (ocstr != NULL) {
+ syslocation = ocstr;
+ syslocation_wr = ocstr;
+ syslocation_len = ocstrlen;
+ syslocation_wr_len = ocstrlen;
+ syslocation_bufsize = bufsize;
+ }
+}
+
+/**
+ * @ingroup snmp_mib2
+ * see \ref snmp_mib2_set_syslocation but set pointer to readonly memory
+ */
+void
+snmp_mib2_set_syslocation_readonly(const u8_t *ocstr, const u16_t *ocstrlen)
+{
+ if (ocstr != NULL) {
+ syslocation = ocstr;
+ syslocation_len = ocstrlen;
+ syslocation_wr = NULL;
+ syslocation_wr_len = NULL;
+ syslocation_bufsize = 0;
+ }
+}
+
+
+static s16_t
+system_get_value(const struct snmp_scalar_array_node_def *node, void *value)
+{
+ const u8_t* var = NULL;
+ const s16_t* var_len;
+ u16_t result;
+
+ switch (node->oid) {
+ case 1: /* sysDescr */
+ var = sysdescr;
+ var_len = (const s16_t*)sysdescr_len;
+ break;
+ case 2: /* sysObjectID */
+ {
+ const struct snmp_obj_id* dev_enterprise_oid = snmp_get_device_enterprise_oid();
+ MEMCPY(value, dev_enterprise_oid->id, dev_enterprise_oid->len * sizeof(u32_t));
+ return dev_enterprise_oid->len * sizeof(u32_t);
+ }
+ case 3: /* sysUpTime */
+ MIB2_COPY_SYSUPTIME_TO((u32_t*)value);
+ return sizeof(u32_t);
+ case 4: /* sysContact */
+ var = syscontact;
+ var_len = (const s16_t*)syscontact_len;
+ break;
+ case 5: /* sysName */
+ var = sysname;
+ var_len = (const s16_t*)sysname_len;
+ break;
+ case 6: /* sysLocation */
+ var = syslocation;
+ var_len = (const s16_t*)syslocation_len;
+ break;
+ case 7: /* sysServices */
+ *(s32_t*)value = SNMP_SYSSERVICES;
+ return sizeof(s32_t);
+ default:
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("system_get_value(): unknown id: %"S32_F"\n", node->oid));
+ return 0;
+ }
+
+ /* handle string values (OID 1,4,5 and 6) */
+ LWIP_ASSERT("", (value != NULL));
+ if (var_len == NULL) {
+ result = (s16_t)strlen((const char*)var);
+ } else {
+ result = *var_len;
+ }
+ MEMCPY(value, var, result);
+ return result;
+}
+
+static snmp_err_t
+system_set_test(const struct snmp_scalar_array_node_def *node, u16_t len, void *value)
+{
+ snmp_err_t ret = SNMP_ERR_WRONGVALUE;
+ const u16_t* var_bufsize = NULL;
+ const u16_t* var_wr_len;
+
+ LWIP_UNUSED_ARG(value);
+
+ switch (node->oid) {
+ case 4: /* sysContact */
+ var_bufsize = &syscontact_bufsize;
+ var_wr_len = syscontact_wr_len;
+ break;
+ case 5: /* sysName */
+ var_bufsize = &sysname_bufsize;
+ var_wr_len = sysname_wr_len;
+ break;
+ case 6: /* sysLocation */
+ var_bufsize = &syslocation_bufsize;
+ var_wr_len = syslocation_wr_len;
+ break;
+ default:
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("system_set_test(): unknown id: %"S32_F"\n", node->oid));
+ return ret;
+ }
+
+ /* check if value is writable at all */
+ if (*var_bufsize > 0) {
+ if (var_wr_len == NULL) {
+ /* we have to take the terminating 0 into account */
+ if (len < *var_bufsize) {
+ ret = SNMP_ERR_NOERROR;
+ }
+ } else {
+ if (len <= *var_bufsize) {
+ ret = SNMP_ERR_NOERROR;
+ }
+ }
+ } else {
+ ret = SNMP_ERR_NOTWRITABLE;
+ }
+
+ return ret;
+}
+
+static snmp_err_t
+system_set_value(const struct snmp_scalar_array_node_def *node, u16_t len, void *value)
+{
+ u8_t* var_wr = NULL;
+ u16_t* var_wr_len;
+
+ switch (node->oid) {
+ case 4: /* sysContact */
+ var_wr = syscontact_wr;
+ var_wr_len = syscontact_wr_len;
+ break;
+ case 5: /* sysName */
+ var_wr = sysname_wr;
+ var_wr_len = sysname_wr_len;
+ break;
+ case 6: /* sysLocation */
+ var_wr = syslocation_wr;
+ var_wr_len = syslocation_wr_len;
+ break;
+ default:
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("system_set_value(): unknown id: %"S32_F"\n", node->oid));
+ return SNMP_ERR_GENERROR;
+ }
+
+ /* no need to check size of target buffer, this was already done in set_test method */
+ LWIP_ASSERT("", var_wr != NULL);
+ MEMCPY(var_wr, value, len);
+
+ if (var_wr_len == NULL) {
+ /* add terminating 0 */
+ var_wr[len] = 0;
+ } else {
+ *var_wr_len = len;
+ }
+
+ return SNMP_ERR_NOERROR;
+}
+
+static const struct snmp_scalar_array_node_def system_nodes[] = {
+ {1, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_ONLY}, /* sysDescr */
+ {2, SNMP_ASN1_TYPE_OBJECT_ID, SNMP_NODE_INSTANCE_READ_ONLY}, /* sysObjectID */
+ {3, SNMP_ASN1_TYPE_TIMETICKS, SNMP_NODE_INSTANCE_READ_ONLY}, /* sysUpTime */
+ {4, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_WRITE}, /* sysContact */
+ {5, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_WRITE}, /* sysName */
+ {6, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_WRITE}, /* sysLocation */
+ {7, SNMP_ASN1_TYPE_INTEGER, SNMP_NODE_INSTANCE_READ_ONLY} /* sysServices */
+};
+
+const struct snmp_scalar_array_node snmp_mib2_system_node = SNMP_SCALAR_CREATE_ARRAY_NODE(1, system_nodes, system_get_value, system_set_test, system_set_value);
+
+#endif /* LWIP_SNMP && SNMP_LWIP_MIB2 */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_mib2_tcp.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_mib2_tcp.c
new file mode 100644
index 0000000..21f6965
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_mib2_tcp.c
@@ -0,0 +1,594 @@
+/**
+ * @file
+ * Management Information Base II (RFC1213) TCP objects and 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: Dirk Ziegelmeier <dziegel@gmx.de>
+ * Christiaan Simons <christiaan.simons@axon.tv>
+ */
+
+#include "lwip/snmp.h"
+#include "lwip/apps/snmp.h"
+#include "lwip/apps/snmp_core.h"
+#include "lwip/apps/snmp_mib2.h"
+#include "lwip/apps/snmp_table.h"
+#include "lwip/apps/snmp_scalar.h"
+#include "lwip/tcp.h"
+#include "lwip/priv/tcp_priv.h"
+#include "lwip/stats.h"
+
+#include <string.h>
+
+#if LWIP_SNMP && SNMP_LWIP_MIB2 && LWIP_TCP
+
+#if SNMP_USE_NETCONN
+#define SYNC_NODE_NAME(node_name) node_name ## _synced
+#define CREATE_LWIP_SYNC_NODE(oid, node_name) \
+ static const struct snmp_threadsync_node node_name ## _synced = SNMP_CREATE_THREAD_SYNC_NODE(oid, &node_name.node, &snmp_mib2_lwip_locks);
+#else
+#define SYNC_NODE_NAME(node_name) node_name
+#define CREATE_LWIP_SYNC_NODE(oid, node_name)
+#endif
+
+/* --- tcp .1.3.6.1.2.1.6 ----------------------------------------------------- */
+
+static s16_t
+tcp_get_value(struct snmp_node_instance* instance, void* value)
+{
+ u32_t *uint_ptr = (u32_t*)value;
+ s32_t *sint_ptr = (s32_t*)value;
+
+ switch (instance->node->oid) {
+ case 1: /* tcpRtoAlgorithm, vanj(4) */
+ *sint_ptr = 4;
+ return sizeof(*sint_ptr);
+ case 2: /* tcpRtoMin */
+ /* @todo not the actual value, a guess,
+ needs to be calculated */
+ *sint_ptr = 1000;
+ return sizeof(*sint_ptr);
+ case 3: /* tcpRtoMax */
+ /* @todo not the actual value, a guess,
+ needs to be calculated */
+ *sint_ptr = 60000;
+ return sizeof(*sint_ptr);
+ case 4: /* tcpMaxConn */
+ *sint_ptr = MEMP_NUM_TCP_PCB;
+ return sizeof(*sint_ptr);
+ case 5: /* tcpActiveOpens */
+ *uint_ptr = STATS_GET(mib2.tcpactiveopens);
+ return sizeof(*uint_ptr);
+ case 6: /* tcpPassiveOpens */
+ *uint_ptr = STATS_GET(mib2.tcppassiveopens);
+ return sizeof(*uint_ptr);
+ case 7: /* tcpAttemptFails */
+ *uint_ptr = STATS_GET(mib2.tcpattemptfails);
+ return sizeof(*uint_ptr);
+ case 8: /* tcpEstabResets */
+ *uint_ptr = STATS_GET(mib2.tcpestabresets);
+ return sizeof(*uint_ptr);
+ 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;
+ }
+ return sizeof(*uint_ptr);
+ case 10: /* tcpInSegs */
+ *uint_ptr = STATS_GET(mib2.tcpinsegs);
+ return sizeof(*uint_ptr);
+ case 11: /* tcpOutSegs */
+ *uint_ptr = STATS_GET(mib2.tcpoutsegs);
+ return sizeof(*uint_ptr);
+ case 12: /* tcpRetransSegs */
+ *uint_ptr = STATS_GET(mib2.tcpretranssegs);
+ return sizeof(*uint_ptr);
+ case 14: /* tcpInErrs */
+ *uint_ptr = STATS_GET(mib2.tcpinerrs);
+ return sizeof(*uint_ptr);
+ case 15: /* tcpOutRsts */
+ *uint_ptr = STATS_GET(mib2.tcpoutrsts);
+ return sizeof(*uint_ptr);
+ case 17: /* tcpHCInSegs */
+ memset(value, 0, 2*sizeof(u32_t)); /* not supported */
+ return 2*sizeof(u32_t);
+ case 18: /* tcpHCOutSegs */
+ memset(value, 0, 2*sizeof(u32_t)); /* not supported */
+ return 2*sizeof(u32_t);
+ default:
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("tcp_get_value(): unknown id: %"S32_F"\n", instance->node->oid));
+ break;
+ }
+
+ return 0;
+}
+
+/* --- tcpConnTable --- */
+
+#if LWIP_IPV4
+
+/* list of allowed value ranges for incoming OID */
+static const struct snmp_oid_range tcp_ConnTable_oid_ranges[] = {
+ { 0, 0xff }, /* IP A */
+ { 0, 0xff }, /* IP B */
+ { 0, 0xff }, /* IP C */
+ { 0, 0xff }, /* IP D */
+ { 0, 0xffff }, /* Port */
+ { 0, 0xff }, /* IP A */
+ { 0, 0xff }, /* IP B */
+ { 0, 0xff }, /* IP C */
+ { 0, 0xff }, /* IP D */
+ { 0, 0xffff } /* Port */
+};
+
+static snmp_err_t
+tcp_ConnTable_get_cell_value_core(struct tcp_pcb *pcb, const u32_t* column, union snmp_variant_value* value, u32_t* value_len)
+{
+ LWIP_UNUSED_ARG(value_len);
+
+ /* value */
+ switch (*column) {
+ case 1: /* tcpConnState */
+ value->u32 = pcb->state + 1;
+ break;
+ case 2: /* tcpConnLocalAddress */
+ value->u32 = ip_2_ip4(&pcb->local_ip)->addr;
+ break;
+ case 3: /* tcpConnLocalPort */
+ value->u32 = pcb->local_port;
+ break;
+ case 4: /* tcpConnRemAddress */
+ if (pcb->state == LISTEN) {
+ value->u32 = IP4_ADDR_ANY4->addr;
+ } else {
+ value->u32 = ip_2_ip4(&pcb->remote_ip)->addr;
+ }
+ break;
+ case 5: /* tcpConnRemPort */
+ if (pcb->state == LISTEN) {
+ value->u32 = 0;
+ } else {
+ value->u32 = pcb->remote_port;
+ }
+ break;
+ default:
+ LWIP_ASSERT("invalid id", 0);
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ return SNMP_ERR_NOERROR;
+}
+
+static snmp_err_t
+tcp_ConnTable_get_cell_value(const u32_t* column, const u32_t* row_oid, u8_t row_oid_len, union snmp_variant_value* value, u32_t* value_len)
+{
+ u8_t i;
+ ip4_addr_t local_ip;
+ ip4_addr_t remote_ip;
+ u16_t local_port;
+ u16_t remote_port;
+ struct tcp_pcb *pcb;
+
+ /* check if incoming OID length and if values are in plausible range */
+ if (!snmp_oid_in_range(row_oid, row_oid_len, tcp_ConnTable_oid_ranges, LWIP_ARRAYSIZE(tcp_ConnTable_oid_ranges))) {
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ /* get IPs and ports from incoming OID */
+ snmp_oid_to_ip4(&row_oid[0], &local_ip); /* we know it succeeds because of oid_in_range check above */
+ local_port = (u16_t)row_oid[4];
+ snmp_oid_to_ip4(&row_oid[5], &remote_ip); /* we know it succeeds because of oid_in_range check above */
+ remote_port = (u16_t)row_oid[9];
+
+ /* find tcp_pcb with requested ips and ports */
+ for (i = 0; i < LWIP_ARRAYSIZE(tcp_pcb_lists); i++) {
+ pcb = *tcp_pcb_lists[i];
+
+ while (pcb != NULL) {
+ /* do local IP and local port match? */
+ if (IP_IS_V4_VAL(pcb->local_ip) &&
+ ip4_addr_cmp(&local_ip, ip_2_ip4(&pcb->local_ip)) && (local_port == pcb->local_port)) {
+
+ /* PCBs in state LISTEN are not connected and have no remote_ip or remote_port */
+ if (pcb->state == LISTEN) {
+ if (ip4_addr_cmp(&remote_ip, IP4_ADDR_ANY4) && (remote_port == 0)) {
+ /* fill in object properties */
+ return tcp_ConnTable_get_cell_value_core(pcb, column, value, value_len);
+ }
+ } else {
+ if (IP_IS_V4_VAL(pcb->remote_ip) &&
+ ip4_addr_cmp(&remote_ip, ip_2_ip4(&pcb->remote_ip)) && (remote_port == pcb->remote_port)) {
+ /* fill in object properties */
+ return tcp_ConnTable_get_cell_value_core(pcb, column, value, value_len);
+ }
+ }
+ }
+
+ pcb = pcb->next;
+ }
+ }
+
+ /* not found */
+ return SNMP_ERR_NOSUCHINSTANCE;
+}
+
+static snmp_err_t
+tcp_ConnTable_get_next_cell_instance_and_value(const u32_t* column, struct snmp_obj_id* row_oid, union snmp_variant_value* value, u32_t* value_len)
+{
+ u8_t i;
+ struct tcp_pcb *pcb;
+ struct snmp_next_oid_state state;
+ u32_t result_temp[LWIP_ARRAYSIZE(tcp_ConnTable_oid_ranges)];
+
+ /* init struct to search next oid */
+ snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(tcp_ConnTable_oid_ranges));
+
+ /* iterate over all possible OIDs to find the next one */
+ for (i = 0; i < LWIP_ARRAYSIZE(tcp_pcb_lists); i++) {
+ pcb = *tcp_pcb_lists[i];
+ while (pcb != NULL) {
+ u32_t test_oid[LWIP_ARRAYSIZE(tcp_ConnTable_oid_ranges)];
+
+ if (IP_IS_V4_VAL(pcb->local_ip)) {
+ snmp_ip4_to_oid(ip_2_ip4(&pcb->local_ip), &test_oid[0]);
+ test_oid[4] = pcb->local_port;
+
+ /* PCBs in state LISTEN are not connected and have no remote_ip or remote_port */
+ if (pcb->state == LISTEN) {
+ snmp_ip4_to_oid(IP4_ADDR_ANY4, &test_oid[5]);
+ test_oid[9] = 0;
+ } else {
+ if (IP_IS_V6_VAL(pcb->remote_ip)) { /* should never happen */
+ continue;
+ }
+ snmp_ip4_to_oid(ip_2_ip4(&pcb->remote_ip), &test_oid[5]);
+ test_oid[9] = pcb->remote_port;
+ }
+
+ /* check generated OID: is it a candidate for the next one? */
+ snmp_next_oid_check(&state, test_oid, LWIP_ARRAYSIZE(tcp_ConnTable_oid_ranges), pcb);
+ }
+
+ pcb = pcb->next;
+ }
+ }
+
+ /* did we find a next one? */
+ if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) {
+ snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len);
+ /* fill in object properties */
+ return tcp_ConnTable_get_cell_value_core((struct tcp_pcb*)state.reference, column, value, value_len);
+ }
+
+ /* not found */
+ return SNMP_ERR_NOSUCHINSTANCE;
+}
+
+#endif /* LWIP_IPV4 */
+
+/* --- tcpConnectionTable --- */
+
+static snmp_err_t
+tcp_ConnectionTable_get_cell_value_core(const u32_t* column, struct tcp_pcb *pcb, union snmp_variant_value* value)
+{
+ /* all items except tcpConnectionState and tcpConnectionProcess are declared as not-accessible */
+ switch (*column) {
+ case 7: /* tcpConnectionState */
+ value->u32 = pcb->state + 1;
+ break;
+ case 8: /* tcpConnectionProcess */
+ value->u32 = 0; /* not supported */
+ break;
+ default:
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ return SNMP_ERR_NOERROR;
+}
+
+static snmp_err_t
+tcp_ConnectionTable_get_cell_value(const u32_t* column, const u32_t* row_oid, u8_t row_oid_len, union snmp_variant_value* value, u32_t* value_len)
+{
+ ip_addr_t local_ip, remote_ip;
+ u16_t local_port, remote_port;
+ struct tcp_pcb *pcb;
+ u8_t idx = 0;
+ u8_t i;
+ struct tcp_pcb ** const tcp_pcb_nonlisten_lists[] = {&tcp_bound_pcbs, &tcp_active_pcbs, &tcp_tw_pcbs};
+
+ LWIP_UNUSED_ARG(value_len);
+
+ /* tcpConnectionLocalAddressType + tcpConnectionLocalAddress + tcpConnectionLocalPort */
+ idx += snmp_oid_to_ip_port(&row_oid[idx], row_oid_len-idx, &local_ip, &local_port);
+ if (idx == 0) {
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ /* tcpConnectionRemAddressType + tcpConnectionRemAddress + tcpConnectionRemPort */
+ idx += snmp_oid_to_ip_port(&row_oid[idx], row_oid_len-idx, &remote_ip, &remote_port);
+ if (idx == 0) {
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ /* find tcp_pcb with requested ip and port*/
+ for (i = 0; i < LWIP_ARRAYSIZE(tcp_pcb_nonlisten_lists); i++) {
+ pcb = *tcp_pcb_nonlisten_lists[i];
+
+ while (pcb != NULL) {
+ if (ip_addr_cmp(&local_ip, &pcb->local_ip) &&
+ (local_port == pcb->local_port) &&
+ ip_addr_cmp(&remote_ip, &pcb->remote_ip) &&
+ (remote_port == pcb->remote_port)) {
+ /* fill in object properties */
+ return tcp_ConnectionTable_get_cell_value_core(column, pcb, value);
+ }
+ pcb = pcb->next;
+ }
+ }
+
+ /* not found */
+ return SNMP_ERR_NOSUCHINSTANCE;
+}
+
+static snmp_err_t
+tcp_ConnectionTable_get_next_cell_instance_and_value(const u32_t* column, struct snmp_obj_id* row_oid, union snmp_variant_value* value, u32_t* value_len)
+{
+ struct tcp_pcb *pcb;
+ struct snmp_next_oid_state state;
+ /* 1x tcpConnectionLocalAddressType + 1x OID len + 16x tcpConnectionLocalAddress + 1x tcpConnectionLocalPort
+ * 1x tcpConnectionRemAddressType + 1x OID len + 16x tcpConnectionRemAddress + 1x tcpConnectionRemPort */
+ u32_t result_temp[38];
+ u8_t i;
+ struct tcp_pcb ** const tcp_pcb_nonlisten_lists[] = {&tcp_bound_pcbs, &tcp_active_pcbs, &tcp_tw_pcbs};
+
+ LWIP_UNUSED_ARG(value_len);
+
+ /* init struct to search next oid */
+ snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(result_temp));
+
+ /* iterate over all possible OIDs to find the next one */
+ for (i = 0; i < LWIP_ARRAYSIZE(tcp_pcb_nonlisten_lists); i++) {
+ pcb = *tcp_pcb_nonlisten_lists[i];
+
+ while (pcb != NULL) {
+ u8_t idx = 0;
+ u32_t test_oid[LWIP_ARRAYSIZE(result_temp)];
+
+ /* tcpConnectionLocalAddressType + tcpConnectionLocalAddress + tcpConnectionLocalPort */
+ idx += snmp_ip_port_to_oid(&pcb->local_ip, pcb->local_port, &test_oid[idx]);
+
+ /* tcpConnectionRemAddressType + tcpConnectionRemAddress + tcpConnectionRemPort */
+ idx += snmp_ip_port_to_oid(&pcb->remote_ip, pcb->remote_port, &test_oid[idx]);
+
+ /* check generated OID: is it a candidate for the next one? */
+ snmp_next_oid_check(&state, test_oid, idx, pcb);
+
+ pcb = pcb->next;
+ }
+ }
+
+ /* did we find a next one? */
+ if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) {
+ snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len);
+ /* fill in object properties */
+ return tcp_ConnectionTable_get_cell_value_core(column, (struct tcp_pcb*)state.reference, value);
+ } else {
+ /* not found */
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+}
+
+/* --- tcpListenerTable --- */
+
+static snmp_err_t
+tcp_ListenerTable_get_cell_value_core(const u32_t* column, union snmp_variant_value* value)
+{
+ /* all items except tcpListenerProcess are declared as not-accessible */
+ switch (*column) {
+ case 4: /* tcpListenerProcess */
+ value->u32 = 0; /* not supported */
+ break;
+ default:
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ return SNMP_ERR_NOERROR;
+}
+
+static snmp_err_t
+tcp_ListenerTable_get_cell_value(const u32_t* column, const u32_t* row_oid, u8_t row_oid_len, union snmp_variant_value* value, u32_t* value_len)
+{
+ ip_addr_t local_ip;
+ u16_t local_port;
+ struct tcp_pcb_listen *pcb;
+ u8_t idx = 0;
+
+ LWIP_UNUSED_ARG(value_len);
+
+ /* tcpListenerLocalAddressType + tcpListenerLocalAddress + tcpListenerLocalPort */
+ idx += snmp_oid_to_ip_port(&row_oid[idx], row_oid_len-idx, &local_ip, &local_port);
+ if (idx == 0) {
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ /* find tcp_pcb with requested ip and port*/
+ pcb = tcp_listen_pcbs.listen_pcbs;
+ while (pcb != NULL) {
+ if (ip_addr_cmp(&local_ip, &pcb->local_ip) &&
+ (local_port == pcb->local_port)) {
+ /* fill in object properties */
+ return tcp_ListenerTable_get_cell_value_core(column, value);
+ }
+ pcb = pcb->next;
+ }
+
+ /* not found */
+ return SNMP_ERR_NOSUCHINSTANCE;
+}
+
+static snmp_err_t
+tcp_ListenerTable_get_next_cell_instance_and_value(const u32_t* column, struct snmp_obj_id* row_oid, union snmp_variant_value* value, u32_t* value_len)
+{
+ struct tcp_pcb_listen *pcb;
+ struct snmp_next_oid_state state;
+ /* 1x tcpListenerLocalAddressType + 1x OID len + 16x tcpListenerLocalAddress + 1x tcpListenerLocalPort */
+ u32_t result_temp[19];
+
+ LWIP_UNUSED_ARG(value_len);
+
+ /* init struct to search next oid */
+ snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(result_temp));
+
+ /* iterate over all possible OIDs to find the next one */
+ pcb = tcp_listen_pcbs.listen_pcbs;
+ while (pcb != NULL) {
+ u8_t idx = 0;
+ u32_t test_oid[LWIP_ARRAYSIZE(result_temp)];
+
+ /* tcpListenerLocalAddressType + tcpListenerLocalAddress + tcpListenerLocalPort */
+ idx += snmp_ip_port_to_oid(&pcb->local_ip, pcb->local_port, &test_oid[idx]);
+
+ /* check generated OID: is it a candidate for the next one? */
+ snmp_next_oid_check(&state, test_oid, idx, NULL);
+
+ pcb = pcb->next;
+ }
+
+ /* did we find a next one? */
+ if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) {
+ snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len);
+ /* fill in object properties */
+ return tcp_ListenerTable_get_cell_value_core(column, value);
+ } else {
+ /* not found */
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+}
+
+static const struct snmp_scalar_node tcp_RtoAlgorithm = SNMP_SCALAR_CREATE_NODE_READONLY(1, SNMP_ASN1_TYPE_INTEGER, tcp_get_value);
+static const struct snmp_scalar_node tcp_RtoMin = SNMP_SCALAR_CREATE_NODE_READONLY(2, SNMP_ASN1_TYPE_INTEGER, tcp_get_value);
+static const struct snmp_scalar_node tcp_RtoMax = SNMP_SCALAR_CREATE_NODE_READONLY(3, SNMP_ASN1_TYPE_INTEGER, tcp_get_value);
+static const struct snmp_scalar_node tcp_MaxConn = SNMP_SCALAR_CREATE_NODE_READONLY(4, SNMP_ASN1_TYPE_INTEGER, tcp_get_value);
+static const struct snmp_scalar_node tcp_ActiveOpens = SNMP_SCALAR_CREATE_NODE_READONLY(5, SNMP_ASN1_TYPE_COUNTER, tcp_get_value);
+static const struct snmp_scalar_node tcp_PassiveOpens = SNMP_SCALAR_CREATE_NODE_READONLY(6, SNMP_ASN1_TYPE_COUNTER, tcp_get_value);
+static const struct snmp_scalar_node tcp_AttemptFails = SNMP_SCALAR_CREATE_NODE_READONLY(7, SNMP_ASN1_TYPE_COUNTER, tcp_get_value);
+static const struct snmp_scalar_node tcp_EstabResets = SNMP_SCALAR_CREATE_NODE_READONLY(8, SNMP_ASN1_TYPE_COUNTER, tcp_get_value);
+static const struct snmp_scalar_node tcp_CurrEstab = SNMP_SCALAR_CREATE_NODE_READONLY(9, SNMP_ASN1_TYPE_GAUGE, tcp_get_value);
+static const struct snmp_scalar_node tcp_InSegs = SNMP_SCALAR_CREATE_NODE_READONLY(10, SNMP_ASN1_TYPE_COUNTER, tcp_get_value);
+static const struct snmp_scalar_node tcp_OutSegs = SNMP_SCALAR_CREATE_NODE_READONLY(11, SNMP_ASN1_TYPE_COUNTER, tcp_get_value);
+static const struct snmp_scalar_node tcp_RetransSegs = SNMP_SCALAR_CREATE_NODE_READONLY(12, SNMP_ASN1_TYPE_COUNTER, tcp_get_value);
+static const struct snmp_scalar_node tcp_InErrs = SNMP_SCALAR_CREATE_NODE_READONLY(14, SNMP_ASN1_TYPE_COUNTER, tcp_get_value);
+static const struct snmp_scalar_node tcp_OutRsts = SNMP_SCALAR_CREATE_NODE_READONLY(15, SNMP_ASN1_TYPE_COUNTER, tcp_get_value);
+static const struct snmp_scalar_node tcp_HCInSegs = SNMP_SCALAR_CREATE_NODE_READONLY(17, SNMP_ASN1_TYPE_COUNTER64, tcp_get_value);
+static const struct snmp_scalar_node tcp_HCOutSegs = SNMP_SCALAR_CREATE_NODE_READONLY(18, SNMP_ASN1_TYPE_COUNTER64, tcp_get_value);
+
+#if LWIP_IPV4
+static const struct snmp_table_simple_col_def tcp_ConnTable_columns[] = {
+ { 1, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 }, /* tcpConnState */
+ { 2, SNMP_ASN1_TYPE_IPADDR, SNMP_VARIANT_VALUE_TYPE_U32 }, /* tcpConnLocalAddress */
+ { 3, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 }, /* tcpConnLocalPort */
+ { 4, SNMP_ASN1_TYPE_IPADDR, SNMP_VARIANT_VALUE_TYPE_U32 }, /* tcpConnRemAddress */
+ { 5, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 } /* tcpConnRemPort */
+};
+
+static const struct snmp_table_simple_node tcp_ConnTable = SNMP_TABLE_CREATE_SIMPLE(13, tcp_ConnTable_columns, tcp_ConnTable_get_cell_value, tcp_ConnTable_get_next_cell_instance_and_value);
+#endif /* LWIP_IPV4 */
+
+static const struct snmp_table_simple_col_def tcp_ConnectionTable_columns[] = {
+ /* all items except tcpConnectionState and tcpConnectionProcess are declared as not-accessible */
+ { 7, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 }, /* tcpConnectionState */
+ { 8, SNMP_ASN1_TYPE_UNSIGNED32, SNMP_VARIANT_VALUE_TYPE_U32 } /* tcpConnectionProcess */
+};
+
+static const struct snmp_table_simple_node tcp_ConnectionTable = SNMP_TABLE_CREATE_SIMPLE(19, tcp_ConnectionTable_columns, tcp_ConnectionTable_get_cell_value, tcp_ConnectionTable_get_next_cell_instance_and_value);
+
+
+static const struct snmp_table_simple_col_def tcp_ListenerTable_columns[] = {
+ /* all items except tcpListenerProcess are declared as not-accessible */
+ { 4, SNMP_ASN1_TYPE_UNSIGNED32, SNMP_VARIANT_VALUE_TYPE_U32 } /* tcpListenerProcess */
+};
+
+static const struct snmp_table_simple_node tcp_ListenerTable = SNMP_TABLE_CREATE_SIMPLE(20, tcp_ListenerTable_columns, tcp_ListenerTable_get_cell_value, tcp_ListenerTable_get_next_cell_instance_and_value);
+
+/* the following nodes access variables in LWIP stack from SNMP worker thread and must therefore be synced to LWIP (TCPIP) thread */
+CREATE_LWIP_SYNC_NODE( 1, tcp_RtoAlgorithm)
+CREATE_LWIP_SYNC_NODE( 2, tcp_RtoMin)
+CREATE_LWIP_SYNC_NODE( 3, tcp_RtoMax)
+CREATE_LWIP_SYNC_NODE( 4, tcp_MaxConn)
+CREATE_LWIP_SYNC_NODE( 5, tcp_ActiveOpens)
+CREATE_LWIP_SYNC_NODE( 6, tcp_PassiveOpens)
+CREATE_LWIP_SYNC_NODE( 7, tcp_AttemptFails)
+CREATE_LWIP_SYNC_NODE( 8, tcp_EstabResets)
+CREATE_LWIP_SYNC_NODE( 9, tcp_CurrEstab)
+CREATE_LWIP_SYNC_NODE(10, tcp_InSegs)
+CREATE_LWIP_SYNC_NODE(11, tcp_OutSegs)
+CREATE_LWIP_SYNC_NODE(12, tcp_RetransSegs)
+#if LWIP_IPV4
+CREATE_LWIP_SYNC_NODE(13, tcp_ConnTable)
+#endif /* LWIP_IPV4 */
+CREATE_LWIP_SYNC_NODE(14, tcp_InErrs)
+CREATE_LWIP_SYNC_NODE(15, tcp_OutRsts)
+CREATE_LWIP_SYNC_NODE(17, tcp_HCInSegs)
+CREATE_LWIP_SYNC_NODE(18, tcp_HCOutSegs)
+CREATE_LWIP_SYNC_NODE(19, tcp_ConnectionTable)
+CREATE_LWIP_SYNC_NODE(20, tcp_ListenerTable)
+
+static const struct snmp_node* const tcp_nodes[] = {
+ &SYNC_NODE_NAME(tcp_RtoAlgorithm).node.node,
+ &SYNC_NODE_NAME(tcp_RtoMin).node.node,
+ &SYNC_NODE_NAME(tcp_RtoMax).node.node,
+ &SYNC_NODE_NAME(tcp_MaxConn).node.node,
+ &SYNC_NODE_NAME(tcp_ActiveOpens).node.node,
+ &SYNC_NODE_NAME(tcp_PassiveOpens).node.node,
+ &SYNC_NODE_NAME(tcp_AttemptFails).node.node,
+ &SYNC_NODE_NAME(tcp_EstabResets).node.node,
+ &SYNC_NODE_NAME(tcp_CurrEstab).node.node,
+ &SYNC_NODE_NAME(tcp_InSegs).node.node,
+ &SYNC_NODE_NAME(tcp_OutSegs).node.node,
+ &SYNC_NODE_NAME(tcp_RetransSegs).node.node,
+#if LWIP_IPV4
+ &SYNC_NODE_NAME(tcp_ConnTable).node.node,
+#endif /* LWIP_IPV4 */
+ &SYNC_NODE_NAME(tcp_InErrs).node.node,
+ &SYNC_NODE_NAME(tcp_OutRsts).node.node,
+ &SYNC_NODE_NAME(tcp_HCInSegs).node.node,
+ &SYNC_NODE_NAME(tcp_HCOutSegs).node.node,
+ &SYNC_NODE_NAME(tcp_ConnectionTable).node.node,
+ &SYNC_NODE_NAME(tcp_ListenerTable).node.node
+};
+
+const struct snmp_tree_node snmp_mib2_tcp_root = SNMP_CREATE_TREE_NODE(6, tcp_nodes);
+#endif /* LWIP_SNMP && SNMP_LWIP_MIB2 && LWIP_TCP */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_mib2_udp.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_mib2_udp.c
new file mode 100644
index 0000000..6a983df
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_mib2_udp.c
@@ -0,0 +1,357 @@
+/**
+ * @file
+ * Management Information Base II (RFC1213) UDP objects and 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: Dirk Ziegelmeier <dziegel@gmx.de>
+ * Christiaan Simons <christiaan.simons@axon.tv>
+ */
+
+#include "lwip/snmp.h"
+#include "lwip/apps/snmp.h"
+#include "lwip/apps/snmp_core.h"
+#include "lwip/apps/snmp_mib2.h"
+#include "lwip/apps/snmp_table.h"
+#include "lwip/apps/snmp_scalar.h"
+#include "lwip/udp.h"
+#include "lwip/stats.h"
+
+#include <string.h>
+
+#if LWIP_SNMP && SNMP_LWIP_MIB2 && LWIP_UDP
+
+#if SNMP_USE_NETCONN
+#define SYNC_NODE_NAME(node_name) node_name ## _synced
+#define CREATE_LWIP_SYNC_NODE(oid, node_name) \
+ static const struct snmp_threadsync_node node_name ## _synced = SNMP_CREATE_THREAD_SYNC_NODE(oid, &node_name.node, &snmp_mib2_lwip_locks);
+#else
+#define SYNC_NODE_NAME(node_name) node_name
+#define CREATE_LWIP_SYNC_NODE(oid, node_name)
+#endif
+
+/* --- udp .1.3.6.1.2.1.7 ----------------------------------------------------- */
+
+static s16_t
+udp_get_value(struct snmp_node_instance* instance, void* value)
+{
+ u32_t *uint_ptr = (u32_t*)value;
+
+ switch (instance->node->oid) {
+ case 1: /* udpInDatagrams */
+ *uint_ptr = STATS_GET(mib2.udpindatagrams);
+ return sizeof(*uint_ptr);
+ case 2: /* udpNoPorts */
+ *uint_ptr = STATS_GET(mib2.udpnoports);
+ return sizeof(*uint_ptr);
+ case 3: /* udpInErrors */
+ *uint_ptr = STATS_GET(mib2.udpinerrors);
+ return sizeof(*uint_ptr);
+ case 4: /* udpOutDatagrams */
+ *uint_ptr = STATS_GET(mib2.udpoutdatagrams);
+ return sizeof(*uint_ptr);
+ case 8: /* udpHCInDatagrams */
+ memset(value, 0, 2*sizeof(u32_t)); /* not supported */
+ return 2*sizeof(u32_t);
+ case 9: /* udpHCOutDatagrams */
+ memset(value, 0, 2*sizeof(u32_t)); /* not supported */
+ return 2*sizeof(u32_t);
+ default:
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("udp_get_value(): unknown id: %"S32_F"\n", instance->node->oid));
+ break;
+ }
+
+ return 0;
+}
+
+/* --- udpEndpointTable --- */
+
+static snmp_err_t
+udp_endpointTable_get_cell_value_core(const u32_t* column, union snmp_variant_value* value)
+{
+ /* all items except udpEndpointProcess are declared as not-accessible */
+ switch (*column) {
+ case 8: /* udpEndpointProcess */
+ value->u32 = 0; /* not supported */
+ break;
+ default:
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ return SNMP_ERR_NOERROR;
+}
+
+static snmp_err_t
+udp_endpointTable_get_cell_value(const u32_t* column, const u32_t* row_oid, u8_t row_oid_len, union snmp_variant_value* value, u32_t* value_len)
+{
+ ip_addr_t local_ip, remote_ip;
+ u16_t local_port, remote_port;
+ struct udp_pcb *pcb;
+ u8_t idx = 0;
+
+ LWIP_UNUSED_ARG(value_len);
+
+ /* udpEndpointLocalAddressType + udpEndpointLocalAddress + udpEndpointLocalPort */
+ idx += snmp_oid_to_ip_port(&row_oid[idx], row_oid_len-idx, &local_ip, &local_port);
+ if (idx == 0) {
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ /* udpEndpointRemoteAddressType + udpEndpointRemoteAddress + udpEndpointRemotePort */
+ idx += snmp_oid_to_ip_port(&row_oid[idx], row_oid_len-idx, &remote_ip, &remote_port);
+ if (idx == 0) {
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ /* udpEndpointInstance */
+ if (row_oid_len < (idx+1)) {
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+ if (row_oid[idx] != 0) {
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ /* find udp_pcb with requested ip and port*/
+ pcb = udp_pcbs;
+ while (pcb != NULL) {
+ if (ip_addr_cmp(&local_ip, &pcb->local_ip) &&
+ (local_port == pcb->local_port) &&
+ ip_addr_cmp(&remote_ip, &pcb->remote_ip) &&
+ (remote_port == pcb->remote_port)) {
+ /* fill in object properties */
+ return udp_endpointTable_get_cell_value_core(column, value);
+ }
+ pcb = pcb->next;
+ }
+
+ /* not found */
+ return SNMP_ERR_NOSUCHINSTANCE;
+}
+
+static snmp_err_t
+udp_endpointTable_get_next_cell_instance_and_value(const u32_t* column, struct snmp_obj_id* row_oid, union snmp_variant_value* value, u32_t* value_len)
+{
+ struct udp_pcb *pcb;
+ struct snmp_next_oid_state state;
+ /* 1x udpEndpointLocalAddressType + 1x OID len + 16x udpEndpointLocalAddress + 1x udpEndpointLocalPort +
+ * 1x udpEndpointRemoteAddressType + 1x OID len + 16x udpEndpointRemoteAddress + 1x udpEndpointRemotePort +
+ * 1x udpEndpointInstance = 39
+ */
+ u32_t result_temp[39];
+
+ LWIP_UNUSED_ARG(value_len);
+
+ /* init struct to search next oid */
+ snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(result_temp));
+
+ /* iterate over all possible OIDs to find the next one */
+ pcb = udp_pcbs;
+ while (pcb != NULL) {
+ u32_t test_oid[LWIP_ARRAYSIZE(result_temp)];
+ u8_t idx = 0;
+
+ /* udpEndpointLocalAddressType + udpEndpointLocalAddress + udpEndpointLocalPort */
+ idx += snmp_ip_port_to_oid(&pcb->local_ip, pcb->local_port, &test_oid[idx]);
+
+ /* udpEndpointRemoteAddressType + udpEndpointRemoteAddress + udpEndpointRemotePort */
+ idx += snmp_ip_port_to_oid(&pcb->remote_ip, pcb->remote_port, &test_oid[idx]);
+
+ test_oid[idx] = 0; /* udpEndpointInstance */
+ idx++;
+
+ /* check generated OID: is it a candidate for the next one? */
+ snmp_next_oid_check(&state, test_oid, idx, NULL);
+
+ pcb = pcb->next;
+ }
+
+ /* did we find a next one? */
+ if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) {
+ snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len);
+ /* fill in object properties */
+ return udp_endpointTable_get_cell_value_core(column, value);
+ } else {
+ /* not found */
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+}
+
+/* --- udpTable --- */
+
+#if LWIP_IPV4
+
+/* list of allowed value ranges for incoming OID */
+static const struct snmp_oid_range udp_Table_oid_ranges[] = {
+ { 0, 0xff }, /* IP A */
+ { 0, 0xff }, /* IP B */
+ { 0, 0xff }, /* IP C */
+ { 0, 0xff }, /* IP D */
+ { 1, 0xffff } /* Port */
+};
+
+static snmp_err_t
+udp_Table_get_cell_value_core(struct udp_pcb *pcb, const u32_t* column, union snmp_variant_value* value, u32_t* value_len)
+{
+ LWIP_UNUSED_ARG(value_len);
+
+ switch (*column) {
+ case 1: /* udpLocalAddress */
+ /* set reference to PCB local IP and return a generic node that copies IP4 addresses */
+ value->u32 = ip_2_ip4(&pcb->local_ip)->addr;
+ break;
+ case 2: /* udpLocalPort */
+ /* set reference to PCB local port and return a generic node that copies u16_t values */
+ value->u32 = pcb->local_port;
+ break;
+ default:
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ return SNMP_ERR_NOERROR;
+}
+
+static snmp_err_t
+udp_Table_get_cell_value(const u32_t* column, const u32_t* row_oid, u8_t row_oid_len, union snmp_variant_value* value, u32_t* value_len)
+{
+ ip4_addr_t ip;
+ u16_t port;
+ struct udp_pcb *pcb;
+
+ /* check if incoming OID length and if values are in plausible range */
+ if (!snmp_oid_in_range(row_oid, row_oid_len, udp_Table_oid_ranges, LWIP_ARRAYSIZE(udp_Table_oid_ranges))) {
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ /* get IP and port from incoming OID */
+ snmp_oid_to_ip4(&row_oid[0], &ip); /* we know it succeeds because of oid_in_range check above */
+ port = (u16_t)row_oid[4];
+
+ /* find udp_pcb with requested ip and port*/
+ pcb = udp_pcbs;
+ while (pcb != NULL) {
+ if (IP_IS_V4_VAL(pcb->local_ip)) {
+ if (ip4_addr_cmp(&ip, ip_2_ip4(&pcb->local_ip)) && (port == pcb->local_port)) {
+ /* fill in object properties */
+ return udp_Table_get_cell_value_core(pcb, column, value, value_len);
+ }
+ }
+ pcb = pcb->next;
+ }
+
+ /* not found */
+ return SNMP_ERR_NOSUCHINSTANCE;
+}
+
+static snmp_err_t
+udp_Table_get_next_cell_instance_and_value(const u32_t* column, struct snmp_obj_id* row_oid, union snmp_variant_value* value, u32_t* value_len)
+{
+ struct udp_pcb *pcb;
+ struct snmp_next_oid_state state;
+ u32_t result_temp[LWIP_ARRAYSIZE(udp_Table_oid_ranges)];
+
+ /* init struct to search next oid */
+ snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(udp_Table_oid_ranges));
+
+ /* iterate over all possible OIDs to find the next one */
+ pcb = udp_pcbs;
+ while (pcb != NULL) {
+ u32_t test_oid[LWIP_ARRAYSIZE(udp_Table_oid_ranges)];
+
+ if (IP_IS_V4_VAL(pcb->local_ip)) {
+ snmp_ip4_to_oid(ip_2_ip4(&pcb->local_ip), &test_oid[0]);
+ test_oid[4] = pcb->local_port;
+
+ /* check generated OID: is it a candidate for the next one? */
+ snmp_next_oid_check(&state, test_oid, LWIP_ARRAYSIZE(udp_Table_oid_ranges), pcb);
+ }
+
+ pcb = pcb->next;
+ }
+
+ /* did we find a next one? */
+ if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) {
+ snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len);
+ /* fill in object properties */
+ return udp_Table_get_cell_value_core((struct udp_pcb*)state.reference, column, value, value_len);
+ } else {
+ /* not found */
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+}
+
+#endif /* LWIP_IPV4 */
+
+static const struct snmp_scalar_node udp_inDatagrams = SNMP_SCALAR_CREATE_NODE_READONLY(1, SNMP_ASN1_TYPE_COUNTER, udp_get_value);
+static const struct snmp_scalar_node udp_noPorts = SNMP_SCALAR_CREATE_NODE_READONLY(2, SNMP_ASN1_TYPE_COUNTER, udp_get_value);
+static const struct snmp_scalar_node udp_inErrors = SNMP_SCALAR_CREATE_NODE_READONLY(3, SNMP_ASN1_TYPE_COUNTER, udp_get_value);
+static const struct snmp_scalar_node udp_outDatagrams = SNMP_SCALAR_CREATE_NODE_READONLY(4, SNMP_ASN1_TYPE_COUNTER, udp_get_value);
+static const struct snmp_scalar_node udp_HCInDatagrams = SNMP_SCALAR_CREATE_NODE_READONLY(8, SNMP_ASN1_TYPE_COUNTER64, udp_get_value);
+static const struct snmp_scalar_node udp_HCOutDatagrams = SNMP_SCALAR_CREATE_NODE_READONLY(9, SNMP_ASN1_TYPE_COUNTER64, udp_get_value);
+
+#if LWIP_IPV4
+static const struct snmp_table_simple_col_def udp_Table_columns[] = {
+ { 1, SNMP_ASN1_TYPE_IPADDR, SNMP_VARIANT_VALUE_TYPE_U32 }, /* udpLocalAddress */
+ { 2, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 } /* udpLocalPort */
+};
+static const struct snmp_table_simple_node udp_Table = SNMP_TABLE_CREATE_SIMPLE(5, udp_Table_columns, udp_Table_get_cell_value, udp_Table_get_next_cell_instance_and_value);
+#endif /* LWIP_IPV4 */
+
+static const struct snmp_table_simple_col_def udp_endpointTable_columns[] = {
+ /* all items except udpEndpointProcess are declared as not-accessible */
+ { 8, SNMP_ASN1_TYPE_UNSIGNED32, SNMP_VARIANT_VALUE_TYPE_U32 } /* udpEndpointProcess */
+};
+
+static const struct snmp_table_simple_node udp_endpointTable = SNMP_TABLE_CREATE_SIMPLE(7, udp_endpointTable_columns, udp_endpointTable_get_cell_value, udp_endpointTable_get_next_cell_instance_and_value);
+
+/* the following nodes access variables in LWIP stack from SNMP worker thread and must therefore be synced to LWIP (TCPIP) thread */
+CREATE_LWIP_SYNC_NODE(1, udp_inDatagrams)
+CREATE_LWIP_SYNC_NODE(2, udp_noPorts)
+CREATE_LWIP_SYNC_NODE(3, udp_inErrors)
+CREATE_LWIP_SYNC_NODE(4, udp_outDatagrams)
+#if LWIP_IPV4
+CREATE_LWIP_SYNC_NODE(5, udp_Table)
+#endif /* LWIP_IPV4 */
+CREATE_LWIP_SYNC_NODE(7, udp_endpointTable)
+CREATE_LWIP_SYNC_NODE(8, udp_HCInDatagrams)
+CREATE_LWIP_SYNC_NODE(9, udp_HCOutDatagrams)
+
+static const struct snmp_node* const udp_nodes[] = {
+ &SYNC_NODE_NAME(udp_inDatagrams).node.node,
+ &SYNC_NODE_NAME(udp_noPorts).node.node,
+ &SYNC_NODE_NAME(udp_inErrors).node.node,
+ &SYNC_NODE_NAME(udp_outDatagrams).node.node,
+#if LWIP_IPV4
+ &SYNC_NODE_NAME(udp_Table).node.node,
+#endif /* LWIP_IPV4 */
+ &SYNC_NODE_NAME(udp_endpointTable).node.node,
+ &SYNC_NODE_NAME(udp_HCInDatagrams).node.node,
+ &SYNC_NODE_NAME(udp_HCOutDatagrams).node.node
+};
+
+const struct snmp_tree_node snmp_mib2_udp_root = SNMP_CREATE_TREE_NODE(7, udp_nodes);
+#endif /* LWIP_SNMP && SNMP_LWIP_MIB2 && LWIP_UDP */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_msg.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_msg.c
new file mode 100644
index 0000000..0cb7ca9
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_msg.c
@@ -0,0 +1,1668 @@
+/**
+ * @file
+ * SNMP message processing (RFC1157).
+ */
+
+/*
+ * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
+ * Copyright (c) 2016 Elias Oenal.
+ * 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>
+ * Martin Hentschel <info@cl-soft.de>
+ * Elias Oenal <lwip@eliasoenal.com>
+ */
+
+#include "lwip/apps/snmp_opts.h"
+
+#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
+
+#include "snmp_msg.h"
+#include "snmp_asn1.h"
+#include "snmp_core_priv.h"
+#include "lwip/ip_addr.h"
+#include "lwip/stats.h"
+
+#if LWIP_SNMP_V3
+#include "lwip/apps/snmpv3.h"
+#include "snmpv3_priv.h"
+#ifdef LWIP_SNMPV3_INCLUDE_ENGINE
+#include LWIP_SNMPV3_INCLUDE_ENGINE
+#endif
+#endif
+
+#include <string.h>
+
+/* public (non-static) constants */
+/** SNMP community string */
+const char *snmp_community = SNMP_COMMUNITY;
+/** SNMP community string for write access */
+const char *snmp_community_write = SNMP_COMMUNITY_WRITE;
+/** SNMP community string for sending traps */
+const char *snmp_community_trap = SNMP_COMMUNITY_TRAP;
+
+snmp_write_callback_fct snmp_write_callback = NULL;
+void* snmp_write_callback_arg = NULL;
+
+/**
+ * @ingroup snmp_core
+ * Returns current SNMP community string.
+ * @return current SNMP community string
+ */
+const char *
+snmp_get_community(void)
+{
+ return snmp_community;
+}
+
+/**
+ * @ingroup snmp_core
+ * Sets SNMP community string.
+ * The string itself (its storage) must be valid throughout the whole life of
+ * program (or until it is changed to sth else).
+ *
+ * @param community is a pointer to new community string
+ */
+void
+snmp_set_community(const char * const community)
+{
+ LWIP_ASSERT("community string is too long!", strlen(community) <= SNMP_MAX_COMMUNITY_STR_LEN);
+ snmp_community = community;
+}
+
+/**
+ * @ingroup snmp_core
+ * Returns current SNMP write-access community string.
+ * @return current SNMP write-access community string
+ */
+const char *
+snmp_get_community_write(void)
+{
+ return snmp_community_write;
+}
+
+/**
+ * @ingroup snmp_traps
+ * Returns current SNMP community string used for sending traps.
+ * @return current SNMP community string used for sending traps
+ */
+const char *
+snmp_get_community_trap(void)
+{
+ return snmp_community_trap;
+}
+
+/**
+ * @ingroup snmp_core
+ * Sets SNMP community string for write-access.
+ * The string itself (its storage) must be valid throughout the whole life of
+ * program (or until it is changed to sth else).
+ *
+ * @param community is a pointer to new write-access community string
+ */
+void
+snmp_set_community_write(const char * const community)
+{
+ LWIP_ASSERT("community string must not be NULL", community != NULL);
+ LWIP_ASSERT("community string is too long!", strlen(community) <= SNMP_MAX_COMMUNITY_STR_LEN);
+ snmp_community_write = community;
+}
+
+/**
+ * @ingroup snmp_traps
+ * Sets SNMP community string used for sending traps.
+ * The string itself (its storage) must be valid throughout the whole life of
+ * program (or until it is changed to sth else).
+ *
+ * @param community is a pointer to new trap community string
+ */
+void
+snmp_set_community_trap(const char * const community)
+{
+ LWIP_ASSERT("community string is too long!", strlen(community) <= SNMP_MAX_COMMUNITY_STR_LEN);
+ snmp_community_trap = community;
+}
+
+/**
+ * @ingroup snmp_core
+ * Callback fired on every successful write access
+ */
+void
+snmp_set_write_callback(snmp_write_callback_fct write_callback, void* callback_arg)
+{
+ snmp_write_callback = write_callback;
+ snmp_write_callback_arg = callback_arg;
+}
+
+/* ----------------------------------------------------------------------- */
+/* forward declarations */
+/* ----------------------------------------------------------------------- */
+
+static err_t snmp_process_get_request(struct snmp_request *request);
+static err_t snmp_process_getnext_request(struct snmp_request *request);
+static err_t snmp_process_getbulk_request(struct snmp_request *request);
+static err_t snmp_process_set_request(struct snmp_request *request);
+
+static err_t snmp_parse_inbound_frame(struct snmp_request *request);
+static err_t snmp_prepare_outbound_frame(struct snmp_request *request);
+static err_t snmp_complete_outbound_frame(struct snmp_request *request);
+static void snmp_execute_write_callbacks(struct snmp_request *request);
+
+
+/* ----------------------------------------------------------------------- */
+/* implementation */
+/* ----------------------------------------------------------------------- */
+
+void
+snmp_receive(void *handle, struct pbuf *p, const ip_addr_t *source_ip, u16_t port)
+{
+ err_t err;
+ struct snmp_request request;
+
+ memset(&request, 0, sizeof(request));
+ request.handle = handle;
+ request.source_ip = source_ip;
+ request.source_port = port;
+ request.inbound_pbuf = p;
+
+ snmp_stats.inpkts++;
+
+ err = snmp_parse_inbound_frame(&request);
+ if (err == ERR_OK) {
+ err = snmp_prepare_outbound_frame(&request);
+ if (err == ERR_OK) {
+
+ if (request.error_status == SNMP_ERR_NOERROR) {
+ /* only process frame if we do not already have an error to return (e.g. all readonly) */
+ if (request.request_type == SNMP_ASN1_CONTEXT_PDU_GET_REQ) {
+ err = snmp_process_get_request(&request);
+ } else if (request.request_type == SNMP_ASN1_CONTEXT_PDU_GET_NEXT_REQ) {
+ err = snmp_process_getnext_request(&request);
+ } else if (request.request_type == SNMP_ASN1_CONTEXT_PDU_GET_BULK_REQ) {
+ err = snmp_process_getbulk_request(&request);
+ } else if (request.request_type == SNMP_ASN1_CONTEXT_PDU_SET_REQ) {
+ err = snmp_process_set_request(&request);
+ }
+ }
+
+ if (err == ERR_OK) {
+ err = snmp_complete_outbound_frame(&request);
+
+ if (err == ERR_OK) {
+ err = snmp_sendto(request.handle, request.outbound_pbuf, request.source_ip, request.source_port);
+
+ if ((request.request_type == SNMP_ASN1_CONTEXT_PDU_SET_REQ)
+ && (request.error_status == SNMP_ERR_NOERROR)
+ && (snmp_write_callback != NULL)) {
+ /* raise write notification for all written objects */
+ snmp_execute_write_callbacks(&request);
+ }
+ }
+ }
+ }
+
+ if (request.outbound_pbuf != NULL) {
+ pbuf_free(request.outbound_pbuf);
+ }
+ }
+}
+
+static u8_t
+snmp_msg_getnext_validate_node_inst(struct snmp_node_instance* node_instance, void* validate_arg)
+{
+ if (((node_instance->access & SNMP_NODE_INSTANCE_ACCESS_READ) != SNMP_NODE_INSTANCE_ACCESS_READ) || (node_instance->get_value == NULL)) {
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ if ((node_instance->asn1_type == SNMP_ASN1_TYPE_COUNTER64) && (((struct snmp_request*)validate_arg)->version == SNMP_VERSION_1)) {
+ /* according to RFC 2089 skip Counter64 objects in GetNext requests from v1 clients */
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ return SNMP_ERR_NOERROR;
+}
+
+static void
+snmp_process_varbind(struct snmp_request *request, struct snmp_varbind *vb, u8_t get_next)
+{
+ err_t err;
+ struct snmp_node_instance node_instance;
+ memset(&node_instance, 0, sizeof(node_instance));
+
+ if (get_next) {
+ struct snmp_obj_id result_oid;
+ request->error_status = snmp_get_next_node_instance_from_oid(vb->oid.id, vb->oid.len, snmp_msg_getnext_validate_node_inst, request, &result_oid, &node_instance);
+
+ if (request->error_status == SNMP_ERR_NOERROR) {
+ snmp_oid_assign(&vb->oid, result_oid.id, result_oid.len);
+ }
+ } else {
+ request->error_status = snmp_get_node_instance_from_oid(vb->oid.id, vb->oid.len, &node_instance);
+
+ if (request->error_status == SNMP_ERR_NOERROR) {
+ /* use 'getnext_validate' method for validation to avoid code duplication (some checks have to be executed here) */
+ request->error_status = snmp_msg_getnext_validate_node_inst(&node_instance, request);
+
+ if (request->error_status != SNMP_ERR_NOERROR) {
+ if (node_instance.release_instance != NULL) {
+ node_instance.release_instance(&node_instance);
+ }
+ }
+ }
+ }
+
+ if (request->error_status != SNMP_ERR_NOERROR) {
+ if (request->error_status >= SNMP_VARBIND_EXCEPTION_OFFSET) {
+ if ((request->version == SNMP_VERSION_2c) || request->version == SNMP_VERSION_3) {
+ /* in SNMP v2c a varbind related exception is stored in varbind and not in frame header */
+ vb->type = (SNMP_ASN1_CONTENTTYPE_PRIMITIVE | SNMP_ASN1_CLASS_CONTEXT | (request->error_status & SNMP_VARBIND_EXCEPTION_MASK));
+ vb->value_len = 0;
+
+ err = snmp_append_outbound_varbind(&(request->outbound_pbuf_stream), vb);
+ if (err == ERR_OK) {
+ /* we stored the exception in varbind -> go on */
+ request->error_status = SNMP_ERR_NOERROR;
+ } else if (err == ERR_BUF) {
+ request->error_status = SNMP_ERR_TOOBIG;
+ } else {
+ request->error_status = SNMP_ERR_GENERROR;
+ }
+ }
+ } else {
+ /* according to RFC 1157/1905, all other errors only return genError */
+ request->error_status = SNMP_ERR_GENERROR;
+ }
+ } else {
+ s16_t len = node_instance.get_value(&node_instance, vb->value);
+ vb->type = node_instance.asn1_type;
+
+ if(len >= 0) {
+ vb->value_len = (u16_t)len; /* cast is OK because we checked >= 0 above */
+
+ LWIP_ASSERT("SNMP_MAX_VALUE_SIZE is configured too low", (vb->value_len & ~SNMP_GET_VALUE_RAW_DATA) <= SNMP_MAX_VALUE_SIZE);
+ err = snmp_append_outbound_varbind(&request->outbound_pbuf_stream, vb);
+
+ if (err == ERR_BUF) {
+ request->error_status = SNMP_ERR_TOOBIG;
+ } else if (err != ERR_OK) {
+ request->error_status = SNMP_ERR_GENERROR;
+ }
+ } else {
+ request->error_status = SNMP_ERR_GENERROR;
+ }
+
+ if (node_instance.release_instance != NULL) {
+ node_instance.release_instance(&node_instance);
+ }
+ }
+}
+
+
+/**
+ * Service an internal or external event for SNMP GET.
+ *
+ * @param request points to the associated message process state
+ */
+static err_t
+snmp_process_get_request(struct snmp_request *request)
+{
+ snmp_vb_enumerator_err_t err;
+ struct snmp_varbind vb;
+ vb.value = request->value_buffer;
+
+ LWIP_DEBUGF(SNMP_DEBUG, ("SNMP get request\n"));
+
+ while (request->error_status == SNMP_ERR_NOERROR) {
+ err = snmp_vb_enumerator_get_next(&request->inbound_varbind_enumerator, &vb);
+ if (err == SNMP_VB_ENUMERATOR_ERR_OK) {
+ if ((vb.type == SNMP_ASN1_TYPE_NULL) && (vb.value_len == 0)) {
+ snmp_process_varbind(request, &vb, 0);
+ } else {
+ request->error_status = SNMP_ERR_GENERROR;
+ }
+ } else if (err == SNMP_VB_ENUMERATOR_ERR_EOVB) {
+ /* no more varbinds in request */
+ break;
+ } else if (err == SNMP_VB_ENUMERATOR_ERR_ASN1ERROR) {
+ /* malformed ASN.1, don't answer */
+ return ERR_ARG;
+ } else {
+ request->error_status = SNMP_ERR_GENERROR;
+ }
+ }
+
+ return ERR_OK;
+}
+
+/**
+ * Service an internal or external event for SNMP GET.
+ *
+ * @param request points to the associated message process state
+ */
+static err_t
+snmp_process_getnext_request(struct snmp_request *request)
+{
+ snmp_vb_enumerator_err_t err;
+ struct snmp_varbind vb;
+ vb.value = request->value_buffer;
+
+ LWIP_DEBUGF(SNMP_DEBUG, ("SNMP get-next request\n"));
+
+ while (request->error_status == SNMP_ERR_NOERROR) {
+ err = snmp_vb_enumerator_get_next(&request->inbound_varbind_enumerator, &vb);
+ if (err == SNMP_VB_ENUMERATOR_ERR_OK) {
+ if ((vb.type == SNMP_ASN1_TYPE_NULL) && (vb.value_len == 0)) {
+ snmp_process_varbind(request, &vb, 1);
+ } else {
+ request->error_status = SNMP_ERR_GENERROR;
+ }
+ } else if (err == SNMP_VB_ENUMERATOR_ERR_EOVB) {
+ /* no more varbinds in request */
+ break;
+ } else if (err == SNMP_VB_ENUMERATOR_ERR_ASN1ERROR) {
+ /* malformed ASN.1, don't answer */
+ return ERR_ARG;
+ } else {
+ request->error_status = SNMP_ERR_GENERROR;
+ }
+ }
+
+ return ERR_OK;
+}
+
+/**
+ * Service an internal or external event for SNMP GETBULKT.
+ *
+ * @param request points to the associated message process state
+ */
+static err_t
+snmp_process_getbulk_request(struct snmp_request *request)
+{
+ snmp_vb_enumerator_err_t err;
+ s32_t non_repeaters = request->non_repeaters;
+ s32_t repetitions;
+ u16_t repetition_offset = 0;
+ struct snmp_varbind_enumerator repetition_varbind_enumerator;
+ struct snmp_varbind vb;
+ vb.value = request->value_buffer;
+
+ if (SNMP_LWIP_GETBULK_MAX_REPETITIONS > 0) {
+ repetitions = LWIP_MIN(request->max_repetitions, SNMP_LWIP_GETBULK_MAX_REPETITIONS);
+ } else {
+ repetitions = request->max_repetitions;
+ }
+
+ LWIP_DEBUGF(SNMP_DEBUG, ("SNMP get-bulk request\n"));
+
+ /* process non repeaters and first repetition */
+ while (request->error_status == SNMP_ERR_NOERROR) {
+ if (non_repeaters == 0) {
+ repetition_offset = request->outbound_pbuf_stream.offset;
+
+ if (repetitions == 0) {
+ /* do not resolve repeaters when repetitions is set to 0 */
+ break;
+ }
+ repetitions--;
+ }
+
+ err = snmp_vb_enumerator_get_next(&request->inbound_varbind_enumerator, &vb);
+ if (err == SNMP_VB_ENUMERATOR_ERR_EOVB) {
+ /* no more varbinds in request */
+ break;
+ } else if (err == SNMP_VB_ENUMERATOR_ERR_ASN1ERROR) {
+ /* malformed ASN.1, don't answer */
+ return ERR_ARG;
+ } else if ((err != SNMP_VB_ENUMERATOR_ERR_OK) || (vb.type != SNMP_ASN1_TYPE_NULL) || (vb.value_len != 0)) {
+ request->error_status = SNMP_ERR_GENERROR;
+ } else {
+ snmp_process_varbind(request, &vb, 1);
+ non_repeaters--;
+ }
+ }
+
+ /* process repetitions > 1 */
+ while ((request->error_status == SNMP_ERR_NOERROR) && (repetitions > 0) && (request->outbound_pbuf_stream.offset != repetition_offset)) {
+
+ u8_t all_endofmibview = 1;
+
+ snmp_vb_enumerator_init(&repetition_varbind_enumerator, request->outbound_pbuf, repetition_offset, request->outbound_pbuf_stream.offset - repetition_offset);
+ repetition_offset = request->outbound_pbuf_stream.offset; /* for next loop */
+
+ while (request->error_status == SNMP_ERR_NOERROR) {
+ vb.value = NULL; /* do NOT decode value (we enumerate outbound buffer here, so all varbinds have values assigned) */
+ err = snmp_vb_enumerator_get_next(&repetition_varbind_enumerator, &vb);
+ if (err == SNMP_VB_ENUMERATOR_ERR_OK) {
+ vb.value = request->value_buffer;
+ snmp_process_varbind(request, &vb, 1);
+
+ if (request->error_status != SNMP_ERR_NOERROR) {
+ /* already set correct error-index (here it cannot be taken from inbound varbind enumerator) */
+ request->error_index = request->non_repeaters + repetition_varbind_enumerator.varbind_count;
+ } else if (vb.type != (SNMP_ASN1_CONTENTTYPE_PRIMITIVE | SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTEXT_VARBIND_END_OF_MIB_VIEW)) {
+ all_endofmibview = 0;
+ }
+ } else if (err == SNMP_VB_ENUMERATOR_ERR_EOVB) {
+ /* no more varbinds in request */
+ break;
+ } else {
+ LWIP_DEBUGF(SNMP_DEBUG, ("Very strange, we cannot parse the varbind output that we created just before!"));
+ request->error_status = SNMP_ERR_GENERROR;
+ request->error_index = request->non_repeaters + repetition_varbind_enumerator.varbind_count;
+ }
+ }
+
+ if ((request->error_status == SNMP_ERR_NOERROR) && all_endofmibview) {
+ /* stop when all varbinds in a loop return EndOfMibView */
+ break;
+ }
+
+ repetitions--;
+ }
+
+ if (request->error_status == SNMP_ERR_TOOBIG) {
+ /* for GetBulk it is ok, if not all requested variables fit into the response -> just return the varbinds added so far */
+ request->error_status = SNMP_ERR_NOERROR;
+ }
+
+ return ERR_OK;
+}
+
+/**
+ * Service an internal or external event for SNMP SET.
+ *
+ * @param request points to the associated message process state
+ */
+static err_t
+snmp_process_set_request(struct snmp_request *request)
+{
+ snmp_vb_enumerator_err_t err;
+ struct snmp_varbind vb;
+ vb.value = request->value_buffer;
+
+ LWIP_DEBUGF(SNMP_DEBUG, ("SNMP set request\n"));
+
+ /* perform set test on all objects */
+ while (request->error_status == SNMP_ERR_NOERROR) {
+ err = snmp_vb_enumerator_get_next(&request->inbound_varbind_enumerator, &vb);
+ if (err == SNMP_VB_ENUMERATOR_ERR_OK) {
+ struct snmp_node_instance node_instance;
+ memset(&node_instance, 0, sizeof(node_instance));
+
+ request->error_status = snmp_get_node_instance_from_oid(vb.oid.id, vb.oid.len, &node_instance);
+ if (request->error_status == SNMP_ERR_NOERROR) {
+ if (node_instance.asn1_type != vb.type) {
+ request->error_status = SNMP_ERR_WRONGTYPE;
+ } else if (((node_instance.access & SNMP_NODE_INSTANCE_ACCESS_WRITE) != SNMP_NODE_INSTANCE_ACCESS_WRITE) || (node_instance.set_value == NULL)) {
+ request->error_status = SNMP_ERR_NOTWRITABLE;
+ } else {
+ if (node_instance.set_test != NULL) {
+ request->error_status = node_instance.set_test(&node_instance, vb.value_len, vb.value);
+ }
+ }
+
+ if (node_instance.release_instance != NULL) {
+ node_instance.release_instance(&node_instance);
+ }
+ }
+ } else if (err == SNMP_VB_ENUMERATOR_ERR_EOVB) {
+ /* no more varbinds in request */
+ break;
+ } else if (err == SNMP_VB_ENUMERATOR_ERR_INVALIDLENGTH) {
+ request->error_status = SNMP_ERR_WRONGLENGTH;
+ } else if (err == SNMP_VB_ENUMERATOR_ERR_ASN1ERROR) {
+ /* malformed ASN.1, don't answer */
+ return ERR_ARG;
+ } else {
+ request->error_status = SNMP_ERR_GENERROR;
+ }
+ }
+
+ /* perform real set operation on all objects */
+ if (request->error_status == SNMP_ERR_NOERROR) {
+ snmp_vb_enumerator_init(&request->inbound_varbind_enumerator, request->inbound_pbuf, request->inbound_varbind_offset, request->inbound_varbind_len);
+ while (request->error_status == SNMP_ERR_NOERROR) {
+ err = snmp_vb_enumerator_get_next(&request->inbound_varbind_enumerator, &vb);
+ if (err == SNMP_VB_ENUMERATOR_ERR_OK) {
+ struct snmp_node_instance node_instance;
+ memset(&node_instance, 0, sizeof(node_instance));
+ request->error_status = snmp_get_node_instance_from_oid(vb.oid.id, vb.oid.len, &node_instance);
+ if (request->error_status == SNMP_ERR_NOERROR) {
+ if (node_instance.set_value(&node_instance, vb.value_len, vb.value) != SNMP_ERR_NOERROR) {
+ if (request->inbound_varbind_enumerator.varbind_count == 1) {
+ request->error_status = SNMP_ERR_COMMITFAILED;
+ } else {
+ /* we cannot undo the set operations done so far */
+ request->error_status = SNMP_ERR_UNDOFAILED;
+ }
+ }
+
+ if (node_instance.release_instance != NULL) {
+ node_instance.release_instance(&node_instance);
+ }
+ }
+ } else if (err == SNMP_VB_ENUMERATOR_ERR_EOVB) {
+ /* no more varbinds in request */
+ break;
+ } else {
+ /* first time enumerating varbinds work but second time not, although nothing should have changed in between ??? */
+ request->error_status = SNMP_ERR_GENERROR;
+ }
+ }
+ }
+
+ return ERR_OK;
+}
+
+#define PARSE_EXEC(code, retValue) \
+ if ((code) != ERR_OK) { \
+ LWIP_DEBUGF(SNMP_DEBUG, ("Malformed ASN.1 detected.\n")); \
+ snmp_stats.inasnparseerrs++; \
+ return retValue; \
+ }
+
+#define PARSE_ASSERT(cond, retValue) \
+ if (!(cond)) { \
+ LWIP_DEBUGF(SNMP_DEBUG, ("SNMP parse assertion failed!: " # cond)); \
+ snmp_stats.inasnparseerrs++; \
+ return retValue; \
+ }
+
+#define BUILD_EXEC(code, retValue) \
+ if ((code) != ERR_OK) { \
+ LWIP_DEBUGF(SNMP_DEBUG, ("SNMP error during creation of outbound frame!: " # code)); \
+ return retValue; \
+ }
+
+#define IF_PARSE_EXEC(code) PARSE_EXEC(code, ERR_ARG)
+#define IF_PARSE_ASSERT(code) PARSE_ASSERT(code, ERR_ARG)
+
+/**
+ * Checks and decodes incoming SNMP message header, logs header errors.
+ *
+ * @param request points to the current message request state return
+ * @return
+ * - ERR_OK SNMP header is sane and accepted
+ * - ERR_VAL SNMP header is either malformed or rejected
+ */
+static err_t
+snmp_parse_inbound_frame(struct snmp_request *request)
+{
+ struct snmp_pbuf_stream pbuf_stream;
+ struct snmp_asn1_tlv tlv;
+ s32_t parent_tlv_value_len;
+ s32_t s32_value;
+ err_t err;
+
+ IF_PARSE_EXEC(snmp_pbuf_stream_init(&pbuf_stream, request->inbound_pbuf, 0, request->inbound_pbuf->tot_len));
+
+ /* decode main container consisting of version, community and PDU */
+ IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+ IF_PARSE_ASSERT((tlv.type == SNMP_ASN1_TYPE_SEQUENCE) && (tlv.value_len == pbuf_stream.length));
+ parent_tlv_value_len = tlv.value_len;
+
+ /* decode version */
+ IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+ IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_INTEGER);
+ parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
+ IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+
+ IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &s32_value));
+ if ((s32_value != SNMP_VERSION_1) &&
+ (s32_value != SNMP_VERSION_2c)
+#if LWIP_SNMP_V3
+ && (s32_value != SNMP_VERSION_3)
+#endif
+ )
+ {
+ /* unsupported SNMP version */
+ snmp_stats.inbadversions++;
+ return ERR_ARG;
+ }
+ request->version = (u8_t)s32_value;
+
+#if LWIP_SNMP_V3
+ if (request->version == SNMP_VERSION_3) {
+ u16_t u16_value;
+ u16_t inbound_msgAuthenticationParameters_offset;
+
+ /* SNMPv3 doesn't use communities */
+ /* @todo: Differentiate read/write access */
+ strcpy((char*)request->community, snmp_community);
+ request->community_strlen = (u16_t)strlen(snmp_community);
+
+ /* RFC3414 globalData */
+ IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+ IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_SEQUENCE);
+ parent_tlv_value_len -= SNMP_ASN1_TLV_HDR_LENGTH(tlv);
+ IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+
+ /* decode msgID */
+ IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+ IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_INTEGER);
+ parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
+ IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+
+ IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &s32_value));
+ request->msg_id = s32_value;
+
+ /* decode msgMaxSize */
+ IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+ IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_INTEGER);
+ parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
+ IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+
+ IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &s32_value));
+ request->msg_max_size = s32_value;
+
+ /* decode msgFlags */
+ IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+ IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_OCTET_STRING);
+ parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
+ IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+
+ IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &s32_value));
+ request->msg_flags = (u8_t)s32_value;
+
+ /* decode msgSecurityModel */
+ IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+ IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_INTEGER);
+ parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
+ IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+
+ IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &s32_value));
+ request->msg_security_model = s32_value;
+
+ /* RFC3414 msgSecurityParameters
+ * The User-based Security Model defines the contents of the OCTET
+ * STRING as a SEQUENCE.
+ *
+ * We skip the protective dummy OCTET STRING header
+ * to access the SEQUENCE header.
+ */
+ IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+ IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_OCTET_STRING);
+ parent_tlv_value_len -= SNMP_ASN1_TLV_HDR_LENGTH(tlv);
+ IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+
+ /* msgSecurityParameters SEQUENCE header */
+ IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+ IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_SEQUENCE);
+ parent_tlv_value_len -= SNMP_ASN1_TLV_HDR_LENGTH(tlv);
+ IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+
+ /* decode msgAuthoritativeEngineID */
+ IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+ IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_OCTET_STRING);
+ parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
+ IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+
+ IF_PARSE_EXEC(snmp_asn1_dec_raw(&pbuf_stream, tlv.value_len, request->msg_authoritative_engine_id,
+ &u16_value, SNMP_V3_MAX_ENGINE_ID_LENGTH));
+ request->msg_authoritative_engine_id_len = (u8_t)u16_value;
+
+ /* msgAuthoritativeEngineBoots */
+ IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+ IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_INTEGER);
+ parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
+ IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+ IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &request->msg_authoritative_engine_boots));
+
+ /* msgAuthoritativeEngineTime */
+ IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+ IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_INTEGER);
+ parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
+ IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+ IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &request->msg_authoritative_engine_time));
+ /* @todo: Implement time window checking */
+
+ /* msgUserName */
+ IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+ IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_OCTET_STRING);
+ parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
+ IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+
+ IF_PARSE_EXEC(snmp_asn1_dec_raw(&pbuf_stream, tlv.value_len, request->msg_user_name,
+ &u16_value, SNMP_V3_MAX_USER_LENGTH));
+ request->msg_user_name_len = (u8_t)u16_value;
+ /* @todo: Implement unknown user error response */
+ IF_PARSE_EXEC(snmpv3_get_user((char*)request->msg_user_name, NULL, NULL, NULL, NULL));
+
+ /* msgAuthenticationParameters */
+ memset(request->msg_authentication_parameters, 0, SNMP_V3_MAX_AUTH_PARAM_LENGTH);
+ IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+ IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_OCTET_STRING);
+ parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
+ IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+ /* Remember position */
+ inbound_msgAuthenticationParameters_offset = pbuf_stream.offset;
+ LWIP_UNUSED_ARG(inbound_msgAuthenticationParameters_offset);
+ /* Read auth parameters */
+ IF_PARSE_ASSERT(tlv.value_len <= SNMP_V3_MAX_AUTH_PARAM_LENGTH);
+ IF_PARSE_EXEC(snmp_asn1_dec_raw(&pbuf_stream, tlv.value_len, request->msg_authentication_parameters,
+ &u16_value, tlv.value_len));
+
+#if LWIP_SNMP_V3_CRYPTO
+ if (request->msg_flags & SNMP_V3_AUTH_FLAG) {
+ const u8_t zero_arr[SNMP_V3_MAX_AUTH_PARAM_LENGTH] = { 0 };
+ u8_t key[20];
+ u8_t algo;
+ u8_t hmac[LWIP_MAX(SNMP_V3_SHA_LEN, SNMP_V3_MD5_LEN)];
+ struct snmp_pbuf_stream auth_stream;
+
+ /* Rewind stream */
+ IF_PARSE_EXEC(snmp_pbuf_stream_init(&pbuf_stream, request->inbound_pbuf, 0, request->inbound_pbuf->tot_len));
+ IF_PARSE_EXEC(snmp_pbuf_stream_seek_abs(&pbuf_stream, inbound_msgAuthenticationParameters_offset));
+ /* Set auth parameters to zero for verification */
+ IF_PARSE_EXEC(snmp_asn1_enc_raw(&pbuf_stream, zero_arr, tlv.value_len));
+
+ /* Verify authentication */
+ IF_PARSE_EXEC(snmp_pbuf_stream_init(&auth_stream, request->inbound_pbuf, 0, request->inbound_pbuf->tot_len));
+
+ IF_PARSE_EXEC(snmpv3_get_user((char*)request->msg_user_name, &algo, key, NULL, NULL));
+ IF_PARSE_EXEC(snmpv3_auth(&auth_stream, request->inbound_pbuf->tot_len, key, algo, hmac));
+ /* @todo: Implement error response */
+ IF_PARSE_EXEC(memcmp(request->msg_authentication_parameters, hmac, SNMP_V3_MAX_AUTH_PARAM_LENGTH));
+ }
+#else
+ /* Ungraceful exit if we encounter cryptography and don't support it.
+ * @todo: Implement error response
+ */
+ IF_PARSE_ASSERT(!(request->msg_flags & (SNMP_V3_AUTH_FLAG | SNMP_V3_PRIV_FLAG)));
+#endif
+
+ /* msgPrivacyParameters */
+ memset(request->msg_privacy_parameters, 0, SNMP_V3_MAX_PRIV_PARAM_LENGTH);
+ IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+ IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_OCTET_STRING);
+ parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
+ IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+
+ IF_PARSE_EXEC(snmp_asn1_dec_raw(&pbuf_stream, tlv.value_len, request->msg_privacy_parameters,
+ &u16_value, SNMP_V3_MAX_PRIV_PARAM_LENGTH));
+
+#if LWIP_SNMP_V3_CRYPTO
+ /* Decrypt message */
+ if (request->msg_flags & SNMP_V3_PRIV_FLAG) {
+ u8_t key[20];
+ u8_t algo;
+
+ IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+ IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_OCTET_STRING);
+ parent_tlv_value_len -= SNMP_ASN1_TLV_HDR_LENGTH(tlv);
+ IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+
+ IF_PARSE_EXEC(snmpv3_get_user((char*)request->msg_user_name, NULL, NULL, &algo, key));
+ IF_PARSE_EXEC(snmpv3_crypt(&pbuf_stream, tlv.value_len, key,
+ request->msg_privacy_parameters, request->msg_authoritative_engine_boots,
+ request->msg_authoritative_engine_time, algo, SNMP_V3_PRIV_MODE_DECRYPT));
+ }
+#endif
+
+ /* Scoped PDU
+ * Encryption context
+ */
+ IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+ IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_SEQUENCE);
+ parent_tlv_value_len -= SNMP_ASN1_TLV_HDR_LENGTH(tlv);
+ IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+
+ /* contextEngineID */
+ IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+ IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_OCTET_STRING);
+ parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
+ IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+
+ IF_PARSE_EXEC(snmp_asn1_dec_raw(&pbuf_stream, tlv.value_len, request->context_engine_id,
+ &u16_value, SNMP_V3_MAX_ENGINE_ID_LENGTH));
+ request->context_engine_id_len = (u8_t)u16_value;
+
+ /* contextName */
+ IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+ IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_OCTET_STRING);
+ parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
+ IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+
+ IF_PARSE_EXEC(snmp_asn1_dec_raw(&pbuf_stream, tlv.value_len, request->context_name,
+ &u16_value, SNMP_V3_MAX_ENGINE_ID_LENGTH));
+ request->context_name_len = (u8_t)u16_value;
+ } else
+#endif
+ {
+ /* decode community */
+ IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+ IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_OCTET_STRING);
+ parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
+ IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+
+ err = snmp_asn1_dec_raw(&pbuf_stream, tlv.value_len, request->community, &request->community_strlen, SNMP_MAX_COMMUNITY_STR_LEN);
+ if (err == ERR_MEM) {
+ /* community string does not fit in our buffer -> its too long -> its invalid */
+ request->community_strlen = 0;
+ snmp_pbuf_stream_seek(&pbuf_stream, tlv.value_len);
+ } else {
+ IF_PARSE_ASSERT(err == ERR_OK);
+ }
+ /* add zero terminator */
+ request->community[request->community_strlen] = 0;
+ }
+
+ /* decode PDU type (next container level) */
+ IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+ IF_PARSE_ASSERT(tlv.value_len <= pbuf_stream.length);
+ request->inbound_padding_len = pbuf_stream.length - tlv.value_len;
+ parent_tlv_value_len = tlv.value_len;
+
+ /* validate PDU type */
+ switch(tlv.type) {
+ case (SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_CONTEXT_PDU_GET_REQ):
+ /* GetRequest PDU */
+ snmp_stats.ingetrequests++;
+ break;
+ case (SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_CONTEXT_PDU_GET_NEXT_REQ):
+ /* GetNextRequest PDU */
+ snmp_stats.ingetnexts++;
+ break;
+ case (SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_CONTEXT_PDU_GET_BULK_REQ):
+ /* GetBulkRequest PDU */
+ if (request->version < SNMP_VERSION_2c) {
+ /* RFC2089: invalid, drop packet */
+ return ERR_ARG;
+ }
+ break;
+ case (SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_CONTEXT_PDU_SET_REQ):
+ /* SetRequest PDU */
+ snmp_stats.insetrequests++;
+ break;
+ default:
+ /* unsupported input PDU for this agent (no parse error) */
+ LWIP_DEBUGF(SNMP_DEBUG, ("Unknown/Invalid SNMP PDU type received: %d", tlv.type)); \
+ return ERR_ARG;
+ break;
+ }
+ request->request_type = tlv.type & SNMP_ASN1_DATATYPE_MASK;
+
+ /* validate community (do this after decoding PDU type because we don't want to increase 'inbadcommunitynames' for wrong frame types */
+ if (request->community_strlen == 0) {
+ /* community string was too long or really empty*/
+ snmp_stats.inbadcommunitynames++;
+ snmp_authfail_trap();
+ return ERR_ARG;
+ } else if (request->request_type == SNMP_ASN1_CONTEXT_PDU_SET_REQ) {
+ if (snmp_community_write[0] == 0) {
+ /* our write community is empty, that means all our objects are readonly */
+ request->error_status = SNMP_ERR_NOTWRITABLE;
+ request->error_index = 1;
+ } else if (strncmp(snmp_community_write, (const char*)request->community, SNMP_MAX_COMMUNITY_STR_LEN) != 0) {
+ /* community name does not match */
+ snmp_stats.inbadcommunitynames++;
+ snmp_authfail_trap();
+ return ERR_ARG;
+ }
+ } else {
+ if (strncmp(snmp_community, (const char*)request->community, SNMP_MAX_COMMUNITY_STR_LEN) != 0) {
+ /* community name does not match */
+ snmp_stats.inbadcommunitynames++;
+ snmp_authfail_trap();
+ return ERR_ARG;
+ }
+ }
+
+ /* decode request ID */
+ IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+ IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_INTEGER);
+ parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
+ IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+
+ IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &request->request_id));
+
+ /* decode error status / non-repeaters */
+ IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+ IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_INTEGER);
+ parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
+ IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+
+ if (request->request_type == SNMP_ASN1_CONTEXT_PDU_GET_BULK_REQ) {
+ IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &request->non_repeaters));
+ if (request->non_repeaters < 0) {
+ /* RFC 1905, 4.2.3 */
+ request->non_repeaters = 0;
+ }
+ } else {
+ /* only check valid value, don't touch 'request->error_status', maybe a response error status was already set to above; */
+ IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &s32_value));
+ IF_PARSE_ASSERT(s32_value == SNMP_ERR_NOERROR);
+ }
+
+ /* decode error index / max-repetitions */
+ IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+ IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_INTEGER);
+ parent_tlv_value_len -= SNMP_ASN1_TLV_LENGTH(tlv);
+ IF_PARSE_ASSERT(parent_tlv_value_len > 0);
+
+ if (request->request_type == SNMP_ASN1_CONTEXT_PDU_GET_BULK_REQ) {
+ IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &request->max_repetitions));
+ if (request->max_repetitions < 0) {
+ /* RFC 1905, 4.2.3 */
+ request->max_repetitions = 0;
+ }
+ } else {
+ IF_PARSE_EXEC(snmp_asn1_dec_s32t(&pbuf_stream, tlv.value_len, &request->error_index));
+ IF_PARSE_ASSERT(s32_value == 0);
+ }
+
+ /* decode varbind-list type (next container level) */
+ IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
+ IF_PARSE_ASSERT((tlv.type == SNMP_ASN1_TYPE_SEQUENCE) && (tlv.value_len <= pbuf_stream.length));
+
+ request->inbound_varbind_offset = pbuf_stream.offset;
+ request->inbound_varbind_len = pbuf_stream.length - request->inbound_padding_len;
+ snmp_vb_enumerator_init(&(request->inbound_varbind_enumerator), request->inbound_pbuf, request->inbound_varbind_offset, request->inbound_varbind_len);
+
+ return ERR_OK;
+}
+
+#define OF_BUILD_EXEC(code) BUILD_EXEC(code, ERR_ARG)
+
+static err_t
+snmp_prepare_outbound_frame(struct snmp_request *request)
+{
+ struct snmp_asn1_tlv tlv;
+ struct snmp_pbuf_stream* pbuf_stream = &(request->outbound_pbuf_stream);
+
+ /* try allocating pbuf(s) for maximum response size */
+ request->outbound_pbuf = pbuf_alloc(PBUF_TRANSPORT, 1472, PBUF_RAM);
+ if (request->outbound_pbuf == NULL) {
+ return ERR_MEM;
+ }
+
+ snmp_pbuf_stream_init(pbuf_stream, request->outbound_pbuf, 0, request->outbound_pbuf->tot_len);
+
+ /* 'Message' sequence */
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 3, 0);
+ OF_BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
+
+ /* version */
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 0);
+ snmp_asn1_enc_s32t_cnt(request->version, &tlv.value_len);
+ OF_BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
+ OF_BUILD_EXEC( snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, request->version) );
+
+#if LWIP_SNMP_V3
+ if (request->version < SNMP_VERSION_3) {
+#endif
+ /* community */
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 0, request->community_strlen);
+ OF_BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
+ OF_BUILD_EXEC( snmp_asn1_enc_raw(pbuf_stream, request->community, request->community_strlen) );
+#if LWIP_SNMP_V3
+ } else {
+ const char* id;
+
+ /* globalData */
+ request->outbound_msg_global_data_offset = pbuf_stream->offset;
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 1, 0);
+ OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+
+ /* msgID */
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 1);
+ snmp_asn1_enc_s32t_cnt(request->msg_id, &tlv.value_len);
+ OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+ OF_BUILD_EXEC(snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, request->msg_id));
+
+ /* msgMaxSize */
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 1);
+ snmp_asn1_enc_s32t_cnt(request->msg_max_size, &tlv.value_len);
+ OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+ OF_BUILD_EXEC(snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, request->msg_max_size));
+
+ /* msgFlags */
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 0, 1);
+ OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+ OF_BUILD_EXEC(snmp_asn1_enc_raw(pbuf_stream, &request->msg_flags, 1));
+
+ /* msgSecurityModel */
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 1);
+ snmp_asn1_enc_s32t_cnt(request->msg_security_model, &tlv.value_len);
+ OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+ OF_BUILD_EXEC(snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, request->msg_security_model));
+
+ /* end of msgGlobalData */
+ request->outbound_msg_global_data_end = pbuf_stream->offset;
+
+ /* msgSecurityParameters */
+ request->outbound_msg_security_parameters_str_offset = pbuf_stream->offset;
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 1, 0);
+ OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+
+ request->outbound_msg_security_parameters_seq_offset = pbuf_stream->offset;
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 1, 0);
+ OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+
+ /* msgAuthoritativeEngineID */
+ snmpv3_get_engine_id(&id, &request->msg_authoritative_engine_id_len);
+ MEMCPY(request->msg_authoritative_engine_id, id, request->msg_authoritative_engine_id_len);
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 0, request->msg_authoritative_engine_id_len);
+ OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+ OF_BUILD_EXEC(snmp_asn1_enc_raw(pbuf_stream, request->msg_authoritative_engine_id, request->msg_authoritative_engine_id_len));
+
+ request->msg_authoritative_engine_time = snmpv3_get_engine_time();
+ request->msg_authoritative_engine_boots = snmpv3_get_engine_boots();
+
+ /* msgAuthoritativeEngineBoots */
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 0);
+ snmp_asn1_enc_s32t_cnt(request->msg_authoritative_engine_boots, &tlv.value_len);
+ OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+ OF_BUILD_EXEC(snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, request->msg_authoritative_engine_boots));
+
+ /* msgAuthoritativeEngineTime */
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 0);
+ snmp_asn1_enc_s32t_cnt(request->msg_authoritative_engine_time, &tlv.value_len);
+ OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+ OF_BUILD_EXEC(snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, request->msg_authoritative_engine_time));
+
+ /* msgUserName */
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 0, request->msg_user_name_len);
+ OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+ OF_BUILD_EXEC(snmp_asn1_enc_raw(pbuf_stream, request->msg_user_name, request->msg_user_name_len));
+
+#if LWIP_SNMP_V3_CRYPTO
+ /* msgAuthenticationParameters */
+ if (request->msg_flags & SNMP_V3_AUTH_FLAG) {
+ memset(request->msg_authentication_parameters, 0, SNMP_V3_MAX_AUTH_PARAM_LENGTH);
+ request->outbound_msg_authentication_parameters_offset = pbuf_stream->offset;
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 1, SNMP_V3_MAX_AUTH_PARAM_LENGTH);
+ OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+ OF_BUILD_EXEC(snmp_asn1_enc_raw(pbuf_stream, request->msg_authentication_parameters, SNMP_V3_MAX_AUTH_PARAM_LENGTH));
+ } else
+#endif
+ {
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 0, 0);
+ OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+ }
+
+#if LWIP_SNMP_V3_CRYPTO
+ /* msgPrivacyParameters */
+ if (request->msg_flags & SNMP_V3_PRIV_FLAG) {
+ snmpv3_build_priv_param(request->msg_privacy_parameters);
+
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 0, SNMP_V3_MAX_PRIV_PARAM_LENGTH);
+ OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+ OF_BUILD_EXEC(snmp_asn1_enc_raw(pbuf_stream, request->msg_privacy_parameters, SNMP_V3_MAX_PRIV_PARAM_LENGTH));
+ } else
+#endif
+ {
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 0, 0);
+ OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
+ }
+
+ /* End of msgSecurityParameters, so we can calculate the length of this sequence later */
+ request->outbound_msg_security_parameters_end = pbuf_stream->offset;
+
+#if LWIP_SNMP_V3_CRYPTO
+ /* For encryption we have to encapsulate the payload in an octet string */
+ if (request->msg_flags & SNMP_V3_PRIV_FLAG) {
+ request->outbound_scoped_pdu_string_offset = pbuf_stream->offset;
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 3, 0);
+ OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+ }
+#endif
+ /* Scoped PDU
+ * Encryption context
+ */
+ request->outbound_scoped_pdu_seq_offset = pbuf_stream->offset;
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 3, 0);
+ OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+
+ /* contextEngineID */
+ snmpv3_get_engine_id(&id, &request->context_engine_id_len);
+ MEMCPY(request->context_engine_id, id, request->context_engine_id_len);
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 0, request->context_engine_id_len);
+ OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+ OF_BUILD_EXEC(snmp_asn1_enc_raw(pbuf_stream, request->context_engine_id, request->context_engine_id_len));
+
+ /* contextName */
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 0, request->context_name_len);
+ OF_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+ OF_BUILD_EXEC(snmp_asn1_enc_raw(pbuf_stream, request->context_name, request->context_name_len));
+ }
+#endif
+
+ /* 'PDU' sequence */
+ request->outbound_pdu_offset = pbuf_stream->offset;
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, (SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_CONTEXT_PDU_GET_RESP), 3, 0);
+ OF_BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
+
+ /* request ID */
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 0);
+ snmp_asn1_enc_s32t_cnt(request->request_id, &tlv.value_len);
+ OF_BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
+ OF_BUILD_EXEC( snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, request->request_id) );
+
+ /* error status */
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 1);
+ OF_BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
+ request->outbound_error_status_offset = pbuf_stream->offset;
+ OF_BUILD_EXEC( snmp_pbuf_stream_write(pbuf_stream, 0) );
+
+ /* error index */
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 1);
+ OF_BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
+ request->outbound_error_index_offset = pbuf_stream->offset;
+ OF_BUILD_EXEC( snmp_pbuf_stream_write(pbuf_stream, 0) );
+
+ /* 'VarBindList' sequence */
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 3, 0);
+ OF_BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
+
+ request->outbound_varbind_offset = pbuf_stream->offset;
+
+ return ERR_OK;
+}
+
+/** Calculate the length of a varbind list */
+err_t
+snmp_varbind_length(struct snmp_varbind *varbind, struct snmp_varbind_len *len)
+{
+ /* calculate required lengths */
+ snmp_asn1_enc_oid_cnt(varbind->oid.id, varbind->oid.len, &len->oid_value_len);
+ snmp_asn1_enc_length_cnt(len->oid_value_len, &len->oid_len_len);
+
+ if (varbind->value_len == 0) {
+ len->value_value_len = 0;
+ } else if (varbind->value_len & SNMP_GET_VALUE_RAW_DATA) {
+ len->value_value_len = varbind->value_len & (~SNMP_GET_VALUE_RAW_DATA);
+ } else {
+ switch (varbind->type) {
+ case SNMP_ASN1_TYPE_INTEGER:
+ if (varbind->value_len != sizeof (s32_t)) {
+ return ERR_VAL;
+ }
+ snmp_asn1_enc_s32t_cnt(*((s32_t*) varbind->value), &len->value_value_len);
+ break;
+ case SNMP_ASN1_TYPE_COUNTER:
+ case SNMP_ASN1_TYPE_GAUGE:
+ case SNMP_ASN1_TYPE_TIMETICKS:
+ if (varbind->value_len != sizeof (u32_t)) {
+ return ERR_VAL;
+ }
+ snmp_asn1_enc_u32t_cnt(*((u32_t*) varbind->value), &len->value_value_len);
+ break;
+ case SNMP_ASN1_TYPE_OCTET_STRING:
+ case SNMP_ASN1_TYPE_IPADDR:
+ case SNMP_ASN1_TYPE_OPAQUE:
+ len->value_value_len = varbind->value_len;
+ break;
+ case SNMP_ASN1_TYPE_NULL:
+ if (varbind->value_len != 0) {
+ return ERR_VAL;
+ }
+ len->value_value_len = 0;
+ break;
+ case SNMP_ASN1_TYPE_OBJECT_ID:
+ if ((varbind->value_len & 0x03) != 0) {
+ return ERR_VAL;
+ }
+ snmp_asn1_enc_oid_cnt((u32_t*) varbind->value, varbind->value_len >> 2, &len->value_value_len);
+ break;
+ case SNMP_ASN1_TYPE_COUNTER64:
+ if (varbind->value_len != (2 * sizeof (u32_t))) {
+ return ERR_VAL;
+ }
+ snmp_asn1_enc_u64t_cnt((u32_t*) varbind->value, &len->value_value_len);
+ break;
+ default:
+ /* unsupported type */
+ return ERR_VAL;
+ }
+ }
+ snmp_asn1_enc_length_cnt(len->value_value_len, &len->value_len_len);
+
+ len->vb_value_len = 1 + len->oid_len_len + len->oid_value_len + 1 + len->value_len_len + len->value_value_len;
+ snmp_asn1_enc_length_cnt(len->vb_value_len, &len->vb_len_len);
+
+ return ERR_OK;
+}
+
+#define OVB_BUILD_EXEC(code) BUILD_EXEC(code, ERR_ARG)
+
+err_t
+snmp_append_outbound_varbind(struct snmp_pbuf_stream *pbuf_stream, struct snmp_varbind* varbind)
+{
+ struct snmp_asn1_tlv tlv;
+ struct snmp_varbind_len len;
+ err_t err;
+
+ err = snmp_varbind_length(varbind, &len);
+
+ if (err != ERR_OK) {
+ return err;
+ }
+
+ /* check length already before adding first data because in case of GetBulk,
+ * data added so far is returned and therefore no partial data shall be added
+ */
+ if ((1 + len.vb_len_len + len.vb_value_len) > pbuf_stream->length) {
+ return ERR_BUF;
+ }
+
+ /* 'VarBind' sequence */
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, len.vb_len_len, len.vb_value_len);
+ OVB_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+
+ /* VarBind OID */
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OBJECT_ID, len.oid_len_len, len.oid_value_len);
+ OVB_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+ OVB_BUILD_EXEC(snmp_asn1_enc_oid(pbuf_stream, varbind->oid.id, varbind->oid.len));
+
+ /* VarBind value */
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, varbind->type, len.value_len_len, len.value_value_len);
+ OVB_BUILD_EXEC(snmp_ans1_enc_tlv(pbuf_stream, &tlv));
+
+ if (len.value_value_len > 0) {
+ if (varbind->value_len & SNMP_GET_VALUE_RAW_DATA) {
+ OVB_BUILD_EXEC(snmp_asn1_enc_raw(pbuf_stream, (u8_t*) varbind->value, len.value_value_len));
+ } else {
+ switch (varbind->type) {
+ case SNMP_ASN1_TYPE_INTEGER:
+ OVB_BUILD_EXEC(snmp_asn1_enc_s32t(pbuf_stream, len.value_value_len, *((s32_t*) varbind->value)));
+ break;
+ case SNMP_ASN1_TYPE_COUNTER:
+ case SNMP_ASN1_TYPE_GAUGE:
+ case SNMP_ASN1_TYPE_TIMETICKS:
+ OVB_BUILD_EXEC(snmp_asn1_enc_u32t(pbuf_stream, len.value_value_len, *((u32_t*) varbind->value)));
+ break;
+ case SNMP_ASN1_TYPE_OCTET_STRING:
+ case SNMP_ASN1_TYPE_IPADDR:
+ case SNMP_ASN1_TYPE_OPAQUE:
+ OVB_BUILD_EXEC(snmp_asn1_enc_raw(pbuf_stream, (u8_t*) varbind->value, len.value_value_len));
+ len.value_value_len = varbind->value_len;
+ break;
+ case SNMP_ASN1_TYPE_OBJECT_ID:
+ OVB_BUILD_EXEC(snmp_asn1_enc_oid(pbuf_stream, (u32_t*) varbind->value, varbind->value_len / sizeof (u32_t)));
+ break;
+ case SNMP_ASN1_TYPE_COUNTER64:
+ OVB_BUILD_EXEC(snmp_asn1_enc_u64t(pbuf_stream, len.value_value_len, (u32_t*) varbind->value));
+ break;
+ default:
+ LWIP_ASSERT("Unknown variable type", 0);
+ break;
+ }
+ }
+ }
+
+ return ERR_OK;
+}
+
+static err_t
+snmp_complete_outbound_frame(struct snmp_request *request)
+{
+ struct snmp_asn1_tlv tlv;
+ u16_t frame_size;
+ u8_t outbound_padding = 0;
+
+ if (request->version == SNMP_VERSION_1) {
+ if (request->error_status != SNMP_ERR_NOERROR) {
+ /* map v2c error codes to v1 compliant error code (according to RFC 2089) */
+ switch (request->error_status) {
+ /* mapping of implementation specific "virtual" error codes
+ * (during processing of frame we already stored them in error_status field,
+ * so no need to check all varbinds here for those exceptions as suggested by RFC) */
+ case SNMP_ERR_NOSUCHINSTANCE:
+ case SNMP_ERR_NOSUCHOBJECT:
+ case SNMP_ERR_ENDOFMIBVIEW:
+ request->error_status = SNMP_ERR_NOSUCHNAME;
+ break;
+ /* mapping according to RFC */
+ case SNMP_ERR_WRONGVALUE:
+ case SNMP_ERR_WRONGENCODING:
+ case SNMP_ERR_WRONGTYPE:
+ case SNMP_ERR_WRONGLENGTH:
+ case SNMP_ERR_INCONSISTENTVALUE:
+ request->error_status = SNMP_ERR_BADVALUE;
+ break;
+ case SNMP_ERR_NOACCESS:
+ case SNMP_ERR_NOTWRITABLE:
+ case SNMP_ERR_NOCREATION:
+ case SNMP_ERR_INCONSISTENTNAME:
+ case SNMP_ERR_AUTHORIZATIONERROR:
+ request->error_status = SNMP_ERR_NOSUCHNAME;
+ break;
+ case SNMP_ERR_RESOURCEUNAVAILABLE:
+ case SNMP_ERR_COMMITFAILED:
+ case SNMP_ERR_UNDOFAILED:
+ default:
+ request->error_status = SNMP_ERR_GENERROR;
+ break;
+ }
+ }
+ } else {
+ if (request->request_type == SNMP_ASN1_CONTEXT_PDU_SET_REQ) {
+ /* map error codes to according to RFC 1905 (4.2.5. The SetRequest-PDU) return 'NotWritable' for unknown OIDs) */
+ switch (request->error_status) {
+ case SNMP_ERR_NOSUCHINSTANCE:
+ case SNMP_ERR_NOSUCHOBJECT:
+ case SNMP_ERR_ENDOFMIBVIEW:
+ request->error_status = SNMP_ERR_NOTWRITABLE;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (request->error_status >= SNMP_VARBIND_EXCEPTION_OFFSET) {
+ /* should never occur because v2 frames store exceptions directly inside varbinds and not as frame error_status */
+ LWIP_DEBUGF(SNMP_DEBUG, ("snmp_complete_outbound_frame() > Found v2 request with varbind exception code stored as error status!\n"));
+ return ERR_ARG;
+ }
+ }
+
+ if ((request->error_status != SNMP_ERR_NOERROR) || (request->request_type == SNMP_ASN1_CONTEXT_PDU_SET_REQ)) {
+ /* all inbound vars are returned in response without any modification for error responses and successful set requests*/
+ struct snmp_pbuf_stream inbound_stream;
+ OF_BUILD_EXEC( snmp_pbuf_stream_init(&inbound_stream, request->inbound_pbuf, request->inbound_varbind_offset, request->inbound_varbind_len) );
+ OF_BUILD_EXEC( snmp_pbuf_stream_init(&(request->outbound_pbuf_stream), request->outbound_pbuf, request->outbound_varbind_offset, request->outbound_pbuf->tot_len - request->outbound_varbind_offset) );
+ snmp_pbuf_stream_writeto(&inbound_stream, &(request->outbound_pbuf_stream), 0);
+ }
+
+ frame_size = request->outbound_pbuf_stream.offset;
+
+#if LWIP_SNMP_V3 && LWIP_SNMP_V3_CRYPTO
+ /* Calculate padding for encryption */
+ if (request->version == SNMP_VERSION_3 && (request->msg_flags & SNMP_V3_PRIV_FLAG)) {
+ u8_t i;
+ outbound_padding = (8 - (u8_t)((frame_size - request->outbound_scoped_pdu_seq_offset) & 0x07)) & 0x07;
+ for (i = 0; i < outbound_padding; i++) {
+ snmp_pbuf_stream_write(&request->outbound_pbuf_stream, 0);
+ }
+ }
+#endif
+
+ /* complete missing length in 'Message' sequence ; 'Message' tlv is located at the beginning (offset 0) */
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 3, frame_size + outbound_padding - 1 - 3); /* - type - length_len(fixed, see snmp_prepare_outbound_frame()) */
+ OF_BUILD_EXEC( snmp_pbuf_stream_init(&(request->outbound_pbuf_stream), request->outbound_pbuf, 0, request->outbound_pbuf->tot_len) );
+ OF_BUILD_EXEC( snmp_ans1_enc_tlv(&(request->outbound_pbuf_stream), &tlv) );
+
+#if LWIP_SNMP_V3
+ if (request->version == SNMP_VERSION_3) {
+ /* complete missing length in 'globalData' sequence */
+ /* - type - length_len(fixed, see snmp_prepare_outbound_frame()) */
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 1, request->outbound_msg_global_data_end
+ - request->outbound_msg_global_data_offset - 1 - 1);
+ OF_BUILD_EXEC(snmp_pbuf_stream_seek_abs(&(request->outbound_pbuf_stream), request->outbound_msg_global_data_offset));
+ OF_BUILD_EXEC(snmp_ans1_enc_tlv(&(request->outbound_pbuf_stream), &tlv));
+
+ /* complete missing length in 'msgSecurityParameters' sequence */
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 1, request->outbound_msg_security_parameters_end
+ - request->outbound_msg_security_parameters_str_offset - 1 - 1);
+ OF_BUILD_EXEC(snmp_pbuf_stream_seek_abs(&(request->outbound_pbuf_stream), request->outbound_msg_security_parameters_str_offset));
+ OF_BUILD_EXEC(snmp_ans1_enc_tlv(&(request->outbound_pbuf_stream), &tlv));
+
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 1, request->outbound_msg_security_parameters_end
+ - request->outbound_msg_security_parameters_seq_offset - 1 - 1);
+ OF_BUILD_EXEC(snmp_pbuf_stream_seek_abs(&(request->outbound_pbuf_stream), request->outbound_msg_security_parameters_seq_offset));
+ OF_BUILD_EXEC(snmp_ans1_enc_tlv(&(request->outbound_pbuf_stream), &tlv));
+
+ /* complete missing length in scoped PDU sequence */
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 3, frame_size - request->outbound_scoped_pdu_seq_offset - 1 - 3);
+ OF_BUILD_EXEC(snmp_pbuf_stream_seek_abs(&(request->outbound_pbuf_stream), request->outbound_scoped_pdu_seq_offset));
+ OF_BUILD_EXEC(snmp_ans1_enc_tlv(&(request->outbound_pbuf_stream), &tlv));
+ }
+#endif
+
+ /* complete missing length in 'PDU' sequence */
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, (SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_CONTEXT_PDU_GET_RESP), 3,
+ frame_size - request->outbound_pdu_offset - 1 - 3); /* - type - length_len(fixed, see snmp_prepare_outbound_frame()) */
+ OF_BUILD_EXEC( snmp_pbuf_stream_seek_abs(&(request->outbound_pbuf_stream), request->outbound_pdu_offset) );
+ OF_BUILD_EXEC( snmp_ans1_enc_tlv(&(request->outbound_pbuf_stream), &tlv) );
+
+ /* process and encode final error status */
+ if (request->error_status != 0) {
+ u16_t len;
+ snmp_asn1_enc_s32t_cnt(request->error_status, &len);
+ if (len != 1) {
+ /* error, we only reserved one byte for it */
+ return ERR_ARG;
+ }
+ OF_BUILD_EXEC( snmp_pbuf_stream_seek_abs(&(request->outbound_pbuf_stream), request->outbound_error_status_offset) );
+ OF_BUILD_EXEC( snmp_asn1_enc_s32t(&(request->outbound_pbuf_stream), len, request->error_status) );
+
+ /* for compatibility to v1, log statistics; in v2 (RFC 1907) these statistics are obsoleted */
+ switch (request->error_status) {
+ case SNMP_ERR_TOOBIG:
+ snmp_stats.outtoobigs++;
+ break;
+ case SNMP_ERR_NOSUCHNAME:
+ snmp_stats.outnosuchnames++;
+ break;
+ case SNMP_ERR_BADVALUE:
+ snmp_stats.outbadvalues++;
+ break;
+ case SNMP_ERR_GENERROR:
+ default:
+ snmp_stats.outgenerrs++;
+ break;
+ }
+
+ if (request->error_status == SNMP_ERR_TOOBIG) {
+ request->error_index = 0; /* defined by RFC 1157 */
+ } else if (request->error_index == 0) {
+ /* set index to varbind where error occured (if not already set before, e.g. during GetBulk processing) */
+ request->error_index = request->inbound_varbind_enumerator.varbind_count;
+ }
+ } else {
+ if (request->request_type == SNMP_ASN1_CONTEXT_PDU_SET_REQ) {
+ snmp_stats.intotalsetvars += request->inbound_varbind_enumerator.varbind_count;
+ } else {
+ snmp_stats.intotalreqvars += request->inbound_varbind_enumerator.varbind_count;
+ }
+ }
+
+ /* encode final error index*/
+ if (request->error_index != 0) {
+ u16_t len;
+ snmp_asn1_enc_s32t_cnt(request->error_index, &len);
+ if (len != 1) {
+ /* error, we only reserved one byte for it */
+ return ERR_VAL;
+ }
+ OF_BUILD_EXEC( snmp_pbuf_stream_seek_abs(&(request->outbound_pbuf_stream), request->outbound_error_index_offset) );
+ OF_BUILD_EXEC( snmp_asn1_enc_s32t(&(request->outbound_pbuf_stream), len, request->error_index) );
+ }
+
+ /* complete missing length in 'VarBindList' sequence ; 'VarBindList' tlv is located directly before varbind offset */
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 3, frame_size - request->outbound_varbind_offset);
+ OF_BUILD_EXEC( snmp_pbuf_stream_seek_abs(&(request->outbound_pbuf_stream), request->outbound_varbind_offset - 1 - 3) ); /* - type - length_len(fixed, see snmp_prepare_outbound_frame()) */
+ OF_BUILD_EXEC( snmp_ans1_enc_tlv(&(request->outbound_pbuf_stream), &tlv) );
+
+ /* Authenticate response */
+#if LWIP_SNMP_V3 && LWIP_SNMP_V3_CRYPTO
+ /* Encrypt response */
+ if (request->version == SNMP_VERSION_3 && (request->msg_flags & SNMP_V3_PRIV_FLAG)) {
+ u8_t key[20];
+ u8_t algo;
+
+ /* complete missing length in PDU sequence */
+ OF_BUILD_EXEC(snmp_pbuf_stream_init(&request->outbound_pbuf_stream, request->outbound_pbuf, 0, request->outbound_pbuf->tot_len));
+ OF_BUILD_EXEC(snmp_pbuf_stream_seek_abs(&(request->outbound_pbuf_stream), request->outbound_scoped_pdu_string_offset));
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 3, frame_size + outbound_padding
+ - request->outbound_scoped_pdu_string_offset - 1 - 3);
+ OF_BUILD_EXEC(snmp_ans1_enc_tlv(&(request->outbound_pbuf_stream), &tlv));
+
+ OF_BUILD_EXEC(snmpv3_get_user((char*)request->msg_user_name, NULL, NULL, &algo, key));
+
+ OF_BUILD_EXEC(snmpv3_crypt(&request->outbound_pbuf_stream, tlv.value_len, key,
+ request->msg_privacy_parameters, request->msg_authoritative_engine_boots,
+ request->msg_authoritative_engine_time, algo, SNMP_V3_PRIV_MODE_ENCRYPT));
+ }
+
+ if (request->version == SNMP_VERSION_3 && (request->msg_flags & SNMP_V3_AUTH_FLAG)) {
+ u8_t key[20];
+ u8_t algo;
+ u8_t hmac[20];
+
+ OF_BUILD_EXEC(snmpv3_get_user((char*)request->msg_user_name, &algo, key, NULL, NULL));
+ OF_BUILD_EXEC(snmp_pbuf_stream_init(&(request->outbound_pbuf_stream),
+ request->outbound_pbuf, 0, request->outbound_pbuf->tot_len));
+ OF_BUILD_EXEC(snmpv3_auth(&request->outbound_pbuf_stream, frame_size + outbound_padding, key, algo, hmac));
+
+ MEMCPY(request->msg_authentication_parameters, hmac, SNMP_V3_MAX_AUTH_PARAM_LENGTH);
+ OF_BUILD_EXEC(snmp_pbuf_stream_init(&request->outbound_pbuf_stream,
+ request->outbound_pbuf, 0, request->outbound_pbuf->tot_len));
+ OF_BUILD_EXEC(snmp_pbuf_stream_seek_abs(&request->outbound_pbuf_stream,
+ request->outbound_msg_authentication_parameters_offset));
+
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 1, SNMP_V3_MAX_AUTH_PARAM_LENGTH);
+ OF_BUILD_EXEC(snmp_ans1_enc_tlv(&request->outbound_pbuf_stream, &tlv));
+ OF_BUILD_EXEC(snmp_asn1_enc_raw(&request->outbound_pbuf_stream,
+ request->msg_authentication_parameters, SNMP_V3_MAX_AUTH_PARAM_LENGTH));
+ }
+#endif
+
+ pbuf_realloc(request->outbound_pbuf, frame_size + outbound_padding);
+
+ snmp_stats.outgetresponses++;
+ snmp_stats.outpkts++;
+
+ return ERR_OK;
+}
+
+static void
+snmp_execute_write_callbacks(struct snmp_request *request)
+{
+ struct snmp_varbind_enumerator inbound_varbind_enumerator;
+ struct snmp_varbind vb;
+
+ snmp_vb_enumerator_init(&inbound_varbind_enumerator, request->inbound_pbuf, request->inbound_varbind_offset, request->inbound_varbind_len);
+ vb.value = NULL; /* do NOT decode value (we enumerate outbound buffer here, so all varbinds have values assigned, which we don't need here) */
+
+ while (snmp_vb_enumerator_get_next(&inbound_varbind_enumerator, &vb) == SNMP_VB_ENUMERATOR_ERR_OK) {
+ snmp_write_callback(vb.oid.id, vb.oid.len, snmp_write_callback_arg);
+ }
+}
+
+
+/* ----------------------------------------------------------------------- */
+/* VarBind enumerator methods */
+/* ----------------------------------------------------------------------- */
+
+void
+snmp_vb_enumerator_init(struct snmp_varbind_enumerator* enumerator, struct pbuf* p, u16_t offset, u16_t length)
+{
+ snmp_pbuf_stream_init(&(enumerator->pbuf_stream), p, offset, length);
+ enumerator->varbind_count = 0;
+}
+
+#define VB_PARSE_EXEC(code) PARSE_EXEC(code, SNMP_VB_ENUMERATOR_ERR_ASN1ERROR)
+#define VB_PARSE_ASSERT(code) PARSE_ASSERT(code, SNMP_VB_ENUMERATOR_ERR_ASN1ERROR)
+
+snmp_vb_enumerator_err_t
+snmp_vb_enumerator_get_next(struct snmp_varbind_enumerator* enumerator, struct snmp_varbind* varbind)
+{
+ struct snmp_asn1_tlv tlv;
+ u16_t varbind_len;
+ err_t err;
+
+ if (enumerator->pbuf_stream.length == 0)
+ {
+ return SNMP_VB_ENUMERATOR_ERR_EOVB;
+ }
+ enumerator->varbind_count++;
+
+ /* decode varbind itself (parent container of a varbind) */
+ VB_PARSE_EXEC(snmp_asn1_dec_tlv(&(enumerator->pbuf_stream), &tlv));
+ VB_PARSE_ASSERT((tlv.type == SNMP_ASN1_TYPE_SEQUENCE) && (tlv.value_len <= enumerator->pbuf_stream.length));
+ varbind_len = tlv.value_len;
+
+ /* decode varbind name (object id) */
+ VB_PARSE_EXEC(snmp_asn1_dec_tlv(&(enumerator->pbuf_stream), &tlv));
+ VB_PARSE_ASSERT((tlv.type == SNMP_ASN1_TYPE_OBJECT_ID) && (SNMP_ASN1_TLV_LENGTH(tlv) < varbind_len) && (tlv.value_len < enumerator->pbuf_stream.length));
+
+ VB_PARSE_EXEC(snmp_asn1_dec_oid(&(enumerator->pbuf_stream), tlv.value_len, varbind->oid.id, &(varbind->oid.len), SNMP_MAX_OBJ_ID_LEN));
+ varbind_len -= SNMP_ASN1_TLV_LENGTH(tlv);
+
+ /* decode varbind value (object id) */
+ VB_PARSE_EXEC(snmp_asn1_dec_tlv(&(enumerator->pbuf_stream), &tlv));
+ VB_PARSE_ASSERT((SNMP_ASN1_TLV_LENGTH(tlv) == varbind_len) && (tlv.value_len <= enumerator->pbuf_stream.length));
+ varbind->type = tlv.type;
+
+ /* shall the value be decoded ? */
+ if (varbind->value != NULL) {
+ switch (varbind->type) {
+ case SNMP_ASN1_TYPE_INTEGER:
+ VB_PARSE_EXEC(snmp_asn1_dec_s32t(&(enumerator->pbuf_stream), tlv.value_len, (s32_t*)varbind->value));
+ varbind->value_len = sizeof(s32_t*);
+ break;
+ case SNMP_ASN1_TYPE_COUNTER:
+ case SNMP_ASN1_TYPE_GAUGE:
+ case SNMP_ASN1_TYPE_TIMETICKS:
+ VB_PARSE_EXEC(snmp_asn1_dec_u32t(&(enumerator->pbuf_stream), tlv.value_len, (u32_t*)varbind->value));
+ varbind->value_len = sizeof(u32_t*);
+ break;
+ case SNMP_ASN1_TYPE_OCTET_STRING:
+ case SNMP_ASN1_TYPE_OPAQUE:
+ err = snmp_asn1_dec_raw(&(enumerator->pbuf_stream), tlv.value_len, (u8_t*)varbind->value, &varbind->value_len, SNMP_MAX_VALUE_SIZE);
+ if (err == ERR_MEM) {
+ return SNMP_VB_ENUMERATOR_ERR_INVALIDLENGTH;
+ }
+ VB_PARSE_ASSERT(err == ERR_OK);
+ break;
+ case SNMP_ASN1_TYPE_NULL:
+ varbind->value_len = 0;
+ break;
+ case SNMP_ASN1_TYPE_OBJECT_ID:
+ /* misuse tlv.length_len as OID_length transporter */
+ err = snmp_asn1_dec_oid(&(enumerator->pbuf_stream), tlv.value_len, (u32_t*)varbind->value, &tlv.length_len, SNMP_MAX_OBJ_ID_LEN);
+ if (err == ERR_MEM) {
+ return SNMP_VB_ENUMERATOR_ERR_INVALIDLENGTH;
+ }
+ VB_PARSE_ASSERT(err == ERR_OK);
+ varbind->value_len = tlv.length_len * sizeof(u32_t);
+ break;
+ case SNMP_ASN1_TYPE_IPADDR:
+ if (tlv.value_len == 4) {
+ /* must be exactly 4 octets! */
+ VB_PARSE_EXEC(snmp_asn1_dec_raw(&(enumerator->pbuf_stream), tlv.value_len, (u8_t*)varbind->value, &varbind->value_len, SNMP_MAX_VALUE_SIZE));
+ } else {
+ VB_PARSE_ASSERT(0);
+ }
+ break;
+ case SNMP_ASN1_TYPE_COUNTER64:
+ VB_PARSE_EXEC(snmp_asn1_dec_u64t(&(enumerator->pbuf_stream), tlv.value_len, (u32_t*)varbind->value));
+ varbind->value_len = 2 * sizeof(u32_t*);
+ break;
+ default:
+ VB_PARSE_ASSERT(0);
+ break;
+ }
+ } else {
+ snmp_pbuf_stream_seek(&(enumerator->pbuf_stream), tlv.value_len);
+ varbind->value_len = tlv.value_len;
+ }
+
+ return SNMP_VB_ENUMERATOR_ERR_OK;
+}
+
+#endif /* LWIP_SNMP */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_msg.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_msg.h
new file mode 100644
index 0000000..2d01ef3
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_msg.h
@@ -0,0 +1,194 @@
+/**
+ * @file
+ * SNMP Agent message handling structures (internal API, do not use in client code).
+ */
+
+/*
+ * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
+ * Copyright (c) 2016 Elias Oenal.
+ * 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>
+ * Martin Hentschel <info@cl-soft.de>
+ * Elias Oenal <lwip@eliasoenal.com>
+ */
+
+#ifndef LWIP_HDR_APPS_SNMP_MSG_H
+#define LWIP_HDR_APPS_SNMP_MSG_H
+
+#include "lwip/apps/snmp_opts.h"
+
+#if LWIP_SNMP
+
+#include "lwip/apps/snmp.h"
+#include "lwip/apps/snmp_core.h"
+#include "snmp_pbuf_stream.h"
+#include "lwip/ip_addr.h"
+#include "lwip/err.h"
+
+#if LWIP_SNMP_V3
+#include "snmpv3_priv.h"
+#endif
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* The listen port of the SNMP agent. Clients have to make their requests to
+ this port. Most standard clients won't work if you change this! */
+#ifndef SNMP_IN_PORT
+#define SNMP_IN_PORT 161
+#endif
+/* The remote port the SNMP agent sends traps to. Most standard trap sinks won't
+ work if you change this! */
+#ifndef SNMP_TRAP_PORT
+#define SNMP_TRAP_PORT 162
+#endif
+
+/* version defines used in PDU */
+#define SNMP_VERSION_1 0
+#define SNMP_VERSION_2c 1
+#define SNMP_VERSION_3 3
+
+struct snmp_varbind_enumerator
+{
+ struct snmp_pbuf_stream pbuf_stream;
+ u16_t varbind_count;
+};
+
+typedef enum {
+ SNMP_VB_ENUMERATOR_ERR_OK = 0,
+ SNMP_VB_ENUMERATOR_ERR_EOVB = 1,
+ SNMP_VB_ENUMERATOR_ERR_ASN1ERROR = 2,
+ SNMP_VB_ENUMERATOR_ERR_INVALIDLENGTH = 3
+} snmp_vb_enumerator_err_t;
+
+void snmp_vb_enumerator_init(struct snmp_varbind_enumerator* enumerator, struct pbuf* p, u16_t offset, u16_t length);
+snmp_vb_enumerator_err_t snmp_vb_enumerator_get_next(struct snmp_varbind_enumerator* enumerator, struct snmp_varbind* varbind);
+
+struct snmp_request
+{
+ /* Communication handle */
+ void *handle;
+ /* source IP address */
+ const ip_addr_t *source_ip;
+ /* source UDP port */
+ u16_t source_port;
+ /* incoming snmp version */
+ u8_t version;
+ /* community name (zero terminated) */
+ u8_t community[SNMP_MAX_COMMUNITY_STR_LEN + 1];
+ /* community string length (exclusive zero term) */
+ u16_t community_strlen;
+ /* request type */
+ u8_t request_type;
+ /* request ID */
+ s32_t request_id;
+ /* error status */
+ s32_t error_status;
+ /* error index */
+ s32_t error_index;
+ /* non-repeaters (getBulkRequest (SNMPv2c)) */
+ s32_t non_repeaters;
+ /* max-repetitions (getBulkRequest (SNMPv2c)) */
+ s32_t max_repetitions;
+
+#if LWIP_SNMP_V3
+ s32_t msg_id;
+ s32_t msg_max_size;
+ u8_t msg_flags;
+ s32_t msg_security_model;
+ u8_t msg_authoritative_engine_id[SNMP_V3_MAX_ENGINE_ID_LENGTH];
+ u8_t msg_authoritative_engine_id_len;
+ s32_t msg_authoritative_engine_boots;
+ s32_t msg_authoritative_engine_time;
+ u8_t msg_user_name[SNMP_V3_MAX_USER_LENGTH];
+ u8_t msg_user_name_len;
+ u8_t msg_authentication_parameters[SNMP_V3_MAX_AUTH_PARAM_LENGTH];
+ u8_t msg_privacy_parameters[SNMP_V3_MAX_PRIV_PARAM_LENGTH];
+ u8_t context_engine_id[SNMP_V3_MAX_ENGINE_ID_LENGTH];
+ u8_t context_engine_id_len;
+ u8_t context_name[SNMP_V3_MAX_ENGINE_ID_LENGTH];
+ u8_t context_name_len;
+#endif
+
+ struct pbuf *inbound_pbuf;
+ struct snmp_varbind_enumerator inbound_varbind_enumerator;
+ u16_t inbound_varbind_offset;
+ u16_t inbound_varbind_len;
+ u16_t inbound_padding_len;
+
+ struct pbuf *outbound_pbuf;
+ struct snmp_pbuf_stream outbound_pbuf_stream;
+ u16_t outbound_pdu_offset;
+ u16_t outbound_error_status_offset;
+ u16_t outbound_error_index_offset;
+ u16_t outbound_varbind_offset;
+#if LWIP_SNMP_V3
+ u16_t outbound_msg_global_data_offset;
+ u16_t outbound_msg_global_data_end;
+ u16_t outbound_msg_security_parameters_str_offset;
+ u16_t outbound_msg_security_parameters_seq_offset;
+ u16_t outbound_msg_security_parameters_end;
+ u16_t outbound_msg_authentication_parameters_offset;
+ u16_t outbound_scoped_pdu_seq_offset;
+ u16_t outbound_scoped_pdu_string_offset;
+#endif
+
+ u8_t value_buffer[SNMP_MAX_VALUE_SIZE];
+};
+
+/** A helper struct keeping length information about varbinds */
+struct snmp_varbind_len
+{
+ u8_t vb_len_len;
+ u16_t vb_value_len;
+ u8_t oid_len_len;
+ u16_t oid_value_len;
+ u8_t value_len_len;
+ u16_t value_value_len;
+};
+
+/** Agent community string */
+extern const char *snmp_community;
+/** Agent community string for write access */
+extern const char *snmp_community_write;
+/** handle for sending traps */
+extern void* snmp_traps_handle;
+
+void snmp_receive(void *handle, struct pbuf *p, const ip_addr_t *source_ip, u16_t port);
+err_t snmp_sendto(void *handle, struct pbuf *p, const ip_addr_t *dst, u16_t port);
+u8_t snmp_get_local_ip_for_dst(void* handle, const ip_addr_t *dst, ip_addr_t *result);
+err_t snmp_varbind_length(struct snmp_varbind *varbind, struct snmp_varbind_len *len);
+err_t snmp_append_outbound_varbind(struct snmp_pbuf_stream *pbuf_stream, struct snmp_varbind* varbind);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_SNMP */
+
+#endif /* LWIP_HDR_APPS_SNMP_MSG_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_netconn.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_netconn.c
new file mode 100644
index 0000000..24c3e26
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_netconn.c
@@ -0,0 +1,121 @@
+/**
+ * @file
+ * SNMP netconn frontend.
+ */
+
+/*
+ * 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.
+ *
+ * Author: Dirk Ziegelmeier <dziegel@gmx.de>
+ */
+
+#include "lwip/apps/snmp_opts.h"
+
+#if LWIP_SNMP && SNMP_USE_NETCONN
+
+#include <string.h>
+#include "lwip/api.h"
+#include "lwip/ip.h"
+#include "lwip/udp.h"
+#include "snmp_msg.h"
+#include "lwip/sys.h"
+
+/** SNMP netconn API worker thread */
+static void
+snmp_netconn_thread(void *arg)
+{
+ struct netconn *conn;
+ struct netbuf *buf;
+ err_t err;
+ LWIP_UNUSED_ARG(arg);
+
+ /* Bind to SNMP port with default IP address */
+#if LWIP_IPV6
+ conn = netconn_new(NETCONN_UDP_IPV6);
+ netconn_bind(conn, IP6_ADDR_ANY, SNMP_IN_PORT);
+#else /* LWIP_IPV6 */
+ conn = netconn_new(NETCONN_UDP);
+ netconn_bind(conn, IP4_ADDR_ANY, SNMP_IN_PORT);
+#endif /* LWIP_IPV6 */
+ LWIP_ERROR("snmp_netconn: invalid conn", (conn != NULL), return;);
+
+ snmp_traps_handle = conn;
+
+ do {
+ err = netconn_recv(conn, &buf);
+
+ if (err == ERR_OK) {
+ snmp_receive(conn, buf->p, &buf->addr, buf->port);
+ }
+
+ if (buf != NULL) {
+ netbuf_delete(buf);
+ }
+ } while(1);
+}
+
+err_t
+snmp_sendto(void *handle, struct pbuf *p, const ip_addr_t *dst, u16_t port)
+{
+ err_t result;
+ struct netbuf buf;
+
+ memset(&buf, 0, sizeof(buf));
+ buf.p = p;
+ result = netconn_sendto((struct netconn*)handle, &buf, dst, port);
+
+ return result;
+}
+
+u8_t
+snmp_get_local_ip_for_dst(void* handle, const ip_addr_t *dst, ip_addr_t *result)
+{
+ struct netconn* conn = (struct netconn*)handle;
+ struct netif *dst_if;
+ const ip_addr_t* dst_ip;
+
+ LWIP_UNUSED_ARG(conn); /* unused in case of IPV4 only configuration */
+
+ ip_route_get_local_ip(&conn->pcb.udp->local_ip, dst, dst_if, dst_ip);
+
+ if ((dst_if != NULL) && (dst_ip != NULL)) {
+ ip_addr_copy(*result, *dst_ip);
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+/**
+ * Starts SNMP Agent.
+ */
+void
+snmp_init(void)
+{
+ sys_thread_new("snmp_netconn", snmp_netconn_thread, NULL, SNMP_STACK_SIZE, SNMP_THREAD_PRIO);
+}
+
+#endif /* LWIP_SNMP && SNMP_USE_NETCONN */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_pbuf_stream.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_pbuf_stream.c
new file mode 100644
index 0000000..3c1217d
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_pbuf_stream.c
@@ -0,0 +1,156 @@
+/**
+ * @file
+ * SNMP pbuf stream wrapper implementation (internal API, do not use in client code).
+ */
+
+/*
+ * 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: Martin Hentschel <info@cl-soft.de>
+ *
+ */
+
+#include "lwip/apps/snmp_opts.h"
+
+#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
+
+#include "snmp_pbuf_stream.h"
+#include "lwip/def.h"
+#include <string.h>
+
+err_t
+snmp_pbuf_stream_init(struct snmp_pbuf_stream* pbuf_stream, struct pbuf* p, u16_t offset, u16_t length)
+{
+ pbuf_stream->offset = offset;
+ pbuf_stream->length = length;
+ pbuf_stream->pbuf = p;
+
+ return ERR_OK;
+}
+
+err_t
+snmp_pbuf_stream_read(struct snmp_pbuf_stream* pbuf_stream, u8_t* data)
+{
+ if (pbuf_stream->length == 0) {
+ return ERR_BUF;
+ }
+
+ if (pbuf_copy_partial(pbuf_stream->pbuf, data, 1, pbuf_stream->offset) == 0) {
+ return ERR_BUF;
+ }
+
+ pbuf_stream->offset++;
+ pbuf_stream->length--;
+
+ return ERR_OK;
+}
+
+err_t
+snmp_pbuf_stream_write(struct snmp_pbuf_stream* pbuf_stream, u8_t data)
+{
+ return snmp_pbuf_stream_writebuf(pbuf_stream, &data, 1);
+}
+
+err_t
+snmp_pbuf_stream_writebuf(struct snmp_pbuf_stream* pbuf_stream, const void* buf, u16_t buf_len)
+{
+ if (pbuf_stream->length < buf_len) {
+ return ERR_BUF;
+ }
+
+ if (pbuf_take_at(pbuf_stream->pbuf, buf, buf_len, pbuf_stream->offset) != ERR_OK) {
+ return ERR_BUF;
+ }
+
+ pbuf_stream->offset += buf_len;
+ pbuf_stream->length -= buf_len;
+
+ return ERR_OK;
+}
+
+err_t
+snmp_pbuf_stream_writeto(struct snmp_pbuf_stream* pbuf_stream, struct snmp_pbuf_stream* target_pbuf_stream, u16_t len)
+{
+
+ if ((pbuf_stream == NULL) || (target_pbuf_stream == NULL)) {
+ return ERR_ARG;
+ }
+ if ((len > pbuf_stream->length) || (len > target_pbuf_stream->length)) {
+ return ERR_ARG;
+ }
+
+ if (len == 0) {
+ len = LWIP_MIN(pbuf_stream->length, target_pbuf_stream->length);
+ }
+
+ while (len > 0) {
+ u16_t chunk_len;
+ err_t err;
+ u16_t target_offset;
+ struct pbuf* pbuf = pbuf_skip(pbuf_stream->pbuf, pbuf_stream->offset, &target_offset);
+
+ if ((pbuf == NULL) || (pbuf->len == 0)) {
+ return ERR_BUF;
+ }
+
+ chunk_len = LWIP_MIN(len, pbuf->len);
+ err = snmp_pbuf_stream_writebuf(target_pbuf_stream, &((u8_t*)pbuf->payload)[target_offset], chunk_len);
+ if (err != ERR_OK) {
+ return err;
+ }
+
+ pbuf_stream->offset += chunk_len;
+ pbuf_stream->length -= chunk_len;
+ len -= chunk_len;
+ }
+
+ return ERR_OK;
+}
+
+err_t
+snmp_pbuf_stream_seek(struct snmp_pbuf_stream* pbuf_stream, s32_t offset)
+{
+ if ((offset < 0) || (offset > pbuf_stream->length)) {
+ /* we cannot seek backwards or forward behind stream end */
+ return ERR_ARG;
+ }
+
+ pbuf_stream->offset += (u16_t)offset;
+ pbuf_stream->length -= (u16_t)offset;
+
+ return ERR_OK;
+}
+
+err_t
+snmp_pbuf_stream_seek_abs(struct snmp_pbuf_stream* pbuf_stream, u32_t offset)
+{
+ s32_t rel_offset = offset - pbuf_stream->offset;
+ return snmp_pbuf_stream_seek(pbuf_stream, rel_offset);
+}
+
+#endif /* LWIP_SNMP */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_pbuf_stream.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_pbuf_stream.h
new file mode 100644
index 0000000..9778de7
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_pbuf_stream.h
@@ -0,0 +1,73 @@
+/**
+ * @file
+ * SNMP pbuf stream wrapper (internal API, do not use in client code).
+ */
+
+/*
+ * 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: Martin Hentschel <info@cl-soft.de>
+ *
+ */
+
+#ifndef LWIP_HDR_APPS_SNMP_PBUF_STREAM_H
+#define LWIP_HDR_APPS_SNMP_PBUF_STREAM_H
+
+#include "lwip/apps/snmp_opts.h"
+
+#if LWIP_SNMP
+
+#include "lwip/err.h"
+#include "lwip/pbuf.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct snmp_pbuf_stream
+{
+ struct pbuf* pbuf;
+ u16_t offset;
+ u16_t length;
+};
+
+err_t snmp_pbuf_stream_init(struct snmp_pbuf_stream* pbuf_stream, struct pbuf* p, u16_t offset, u16_t length);
+err_t snmp_pbuf_stream_read(struct snmp_pbuf_stream* pbuf_stream, u8_t* data);
+err_t snmp_pbuf_stream_write(struct snmp_pbuf_stream* pbuf_stream, u8_t data);
+err_t snmp_pbuf_stream_writebuf(struct snmp_pbuf_stream* pbuf_stream, const void* buf, u16_t buf_len);
+err_t snmp_pbuf_stream_writeto(struct snmp_pbuf_stream* pbuf_stream, struct snmp_pbuf_stream* target_pbuf_stream, u16_t len);
+err_t snmp_pbuf_stream_seek(struct snmp_pbuf_stream* pbuf_stream, s32_t offset);
+err_t snmp_pbuf_stream_seek_abs(struct snmp_pbuf_stream* pbuf_stream, u32_t offset);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_SNMP */
+
+#endif /* LWIP_HDR_APPS_SNMP_PBUF_STREAM_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_raw.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_raw.c
new file mode 100644
index 0000000..4a40864
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_raw.c
@@ -0,0 +1,100 @@
+/**
+ * @file
+ * SNMP RAW API frontend.
+ */
+
+/*
+ * 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.
+ *
+ * Author: Dirk Ziegelmeier <dziegel@gmx.de>
+ */
+
+#include "lwip/apps/snmp_opts.h"
+#include "lwip/ip_addr.h"
+
+#if LWIP_SNMP && SNMP_USE_RAW
+
+#include "lwip/udp.h"
+#include "lwip/ip.h"
+#include "snmp_msg.h"
+
+/* lwIP UDP receive callback function */
+static void
+snmp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
+{
+ LWIP_UNUSED_ARG(arg);
+
+ snmp_receive(pcb, p, addr, port);
+
+ pbuf_free(p);
+}
+
+err_t
+snmp_sendto(void *handle, struct pbuf *p, const ip_addr_t *dst, u16_t port)
+{
+ return udp_sendto((struct udp_pcb*)handle, p, dst, port);
+}
+
+u8_t
+snmp_get_local_ip_for_dst(void* handle, const ip_addr_t *dst, ip_addr_t *result)
+{
+ struct udp_pcb* udp_pcb = (struct udp_pcb*)handle;
+ struct netif *dst_if;
+ const ip_addr_t* dst_ip;
+
+ LWIP_UNUSED_ARG(udp_pcb); /* unused in case of IPV4 only configuration */
+
+ ip_route_get_local_ip(&udp_pcb->local_ip, dst, dst_if, dst_ip);
+
+ if ((dst_if != NULL) && (dst_ip != NULL)) {
+ ip_addr_copy(*result, *dst_ip);
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+/**
+ * @ingroup snmp_core
+ * Starts SNMP Agent.
+ * Allocates UDP pcb and binds it to IP_ANY_TYPE port 161.
+ */
+void
+snmp_init(void)
+{
+ err_t err;
+
+ struct udp_pcb *snmp_pcb = udp_new_ip_type(IPADDR_TYPE_ANY);
+ LWIP_ERROR("snmp_raw: no PCB", (snmp_pcb != NULL), return;);
+
+ snmp_traps_handle = snmp_pcb;
+
+ udp_recv(snmp_pcb, snmp_recv, (void *)SNMP_IN_PORT);
+ err = udp_bind(snmp_pcb, IP_ANY_TYPE, SNMP_IN_PORT);
+ LWIP_ERROR("snmp_raw: Unable to bind PCB", (err == ERR_OK), return;);
+}
+
+#endif /* LWIP_SNMP && SNMP_USE_RAW */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_scalar.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_scalar.c
new file mode 100644
index 0000000..136c9ec
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_scalar.c
@@ -0,0 +1,220 @@
+/**
+ * @file
+ * SNMP scalar node support implementation.
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Martin Hentschel <info@cl-soft.de>
+ *
+ */
+
+#include "lwip/apps/snmp_opts.h"
+
+#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/apps/snmp_scalar.h"
+#include "lwip/apps/snmp_core.h"
+
+static s16_t snmp_scalar_array_get_value(struct snmp_node_instance* instance, void* value);
+static snmp_err_t snmp_scalar_array_set_test(struct snmp_node_instance* instance, u16_t value_len, void* value);
+static snmp_err_t snmp_scalar_array_set_value(struct snmp_node_instance* instance, u16_t value_len, void* value);
+
+snmp_err_t
+snmp_scalar_get_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance)
+{
+ const struct snmp_scalar_node* scalar_node = (const struct snmp_scalar_node*)(const void*)instance->node;
+
+ LWIP_UNUSED_ARG(root_oid);
+ LWIP_UNUSED_ARG(root_oid_len);
+
+ /* scalar only has one dedicated instance: .0 */
+ if ((instance->instance_oid.len != 1) || (instance->instance_oid.id[0] != 0)) {
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ instance->access = scalar_node->access;
+ instance->asn1_type = scalar_node->asn1_type;
+ instance->get_value = scalar_node->get_value;
+ instance->set_test = scalar_node->set_test;
+ instance->set_value = scalar_node->set_value;
+ return SNMP_ERR_NOERROR;
+}
+
+snmp_err_t
+snmp_scalar_get_next_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance)
+{
+ /* because our only instance is .0 we can only return a next instance if no instance oid is passed */
+ if (instance->instance_oid.len == 0) {
+ instance->instance_oid.len = 1;
+ instance->instance_oid.id[0] = 0;
+
+ return snmp_scalar_get_instance(root_oid, root_oid_len, instance);
+ }
+
+ return SNMP_ERR_NOSUCHINSTANCE;
+}
+
+
+snmp_err_t
+snmp_scalar_array_get_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance)
+{
+ LWIP_UNUSED_ARG(root_oid);
+ LWIP_UNUSED_ARG(root_oid_len);
+
+ if ((instance->instance_oid.len == 2) && (instance->instance_oid.id[1] == 0)) {
+ const struct snmp_scalar_array_node* array_node = (const struct snmp_scalar_array_node*)(const void*)instance->node;
+ const struct snmp_scalar_array_node_def* array_node_def = array_node->array_nodes;
+ u32_t i = 0;
+
+ while (i < array_node->array_node_count) {
+ if (array_node_def->oid == instance->instance_oid.id[0]) {
+ break;
+ }
+
+ array_node_def++;
+ i++;
+ }
+
+ if (i < array_node->array_node_count) {
+ instance->access = array_node_def->access;
+ instance->asn1_type = array_node_def->asn1_type;
+ instance->get_value = snmp_scalar_array_get_value;
+ instance->set_test = snmp_scalar_array_set_test;
+ instance->set_value = snmp_scalar_array_set_value;
+ instance->reference.const_ptr = array_node_def;
+
+ return SNMP_ERR_NOERROR;
+ }
+ }
+
+ return SNMP_ERR_NOSUCHINSTANCE;
+}
+
+snmp_err_t
+snmp_scalar_array_get_next_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance)
+{
+ const struct snmp_scalar_array_node* array_node = (const struct snmp_scalar_array_node*)(const void*)instance->node;
+ const struct snmp_scalar_array_node_def* array_node_def = array_node->array_nodes;
+ const struct snmp_scalar_array_node_def* result = NULL;
+
+ LWIP_UNUSED_ARG(root_oid);
+ LWIP_UNUSED_ARG(root_oid_len);
+
+ if ((instance->instance_oid.len == 0) && (array_node->array_node_count > 0)) {
+ /* return node with lowest OID */
+ u16_t i = 0;
+
+ result = array_node_def;
+ array_node_def++;
+
+ for (i = 1; i < array_node->array_node_count; i++) {
+ if (array_node_def->oid < result->oid) {
+ result = array_node_def;
+ }
+ array_node_def++;
+ }
+ } else if (instance->instance_oid.len >= 1) {
+ if (instance->instance_oid.len == 1) {
+ /* if we have the requested OID we return its instance, otherwise we search for the next available */
+ u16_t i = 0;
+ while (i < array_node->array_node_count) {
+ if (array_node_def->oid == instance->instance_oid.id[0]) {
+ result = array_node_def;
+ break;
+ }
+
+ array_node_def++;
+ i++;
+ }
+ }
+ if (result == NULL) {
+ u32_t oid_dist = 0xFFFFFFFFUL;
+ u16_t i = 0;
+ array_node_def = array_node->array_nodes; /* may be already at the end when if case before was executed without result -> reinitialize to start */
+ while (i < array_node->array_node_count) {
+ if ((array_node_def->oid > instance->instance_oid.id[0]) &&
+ ((u32_t)(array_node_def->oid - instance->instance_oid.id[0]) < oid_dist)) {
+ result = array_node_def;
+ oid_dist = array_node_def->oid - instance->instance_oid.id[0];
+ }
+
+ array_node_def++;
+ i++;
+ }
+ }
+ }
+
+ if (result == NULL) {
+ /* nothing to return */
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ instance->instance_oid.len = 2;
+ instance->instance_oid.id[0] = result->oid;
+ instance->instance_oid.id[1] = 0;
+
+ instance->access = result->access;
+ instance->asn1_type = result->asn1_type;
+ instance->get_value = snmp_scalar_array_get_value;
+ instance->set_test = snmp_scalar_array_set_test;
+ instance->set_value = snmp_scalar_array_set_value;
+ instance->reference.const_ptr = result;
+
+ return SNMP_ERR_NOERROR;
+}
+
+static s16_t
+snmp_scalar_array_get_value(struct snmp_node_instance* instance, void* value)
+{
+ const struct snmp_scalar_array_node* array_node = (const struct snmp_scalar_array_node*)(const void*)instance->node;
+ const struct snmp_scalar_array_node_def* array_node_def = (const struct snmp_scalar_array_node_def*)instance->reference.const_ptr;
+
+ return array_node->get_value(array_node_def, value);
+}
+
+static snmp_err_t
+snmp_scalar_array_set_test(struct snmp_node_instance* instance, u16_t value_len, void* value)
+{
+ const struct snmp_scalar_array_node* array_node = (const struct snmp_scalar_array_node*)(const void*)instance->node;
+ const struct snmp_scalar_array_node_def* array_node_def = (const struct snmp_scalar_array_node_def*)instance->reference.const_ptr;
+
+ return array_node->set_test(array_node_def, value_len, value);
+}
+
+static snmp_err_t
+snmp_scalar_array_set_value(struct snmp_node_instance* instance, u16_t value_len, void* value)
+{
+ const struct snmp_scalar_array_node* array_node = (const struct snmp_scalar_array_node*)(const void*)instance->node;
+ const struct snmp_scalar_array_node_def* array_node_def = (const struct snmp_scalar_array_node_def*)instance->reference.const_ptr;
+
+ return array_node->set_value(array_node_def, value_len, value);
+}
+
+#endif /* LWIP_SNMP */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_table.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_table.c
new file mode 100644
index 0000000..63ca595
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_table.c
@@ -0,0 +1,343 @@
+/**
+ * @file
+ * SNMP table support implementation.
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Martin Hentschel <info@cl-soft.de>
+ *
+ */
+
+#include "lwip/apps/snmp_opts.h"
+
+#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/apps/snmp_core.h"
+#include "lwip/apps/snmp_table.h"
+#include <string.h>
+
+snmp_err_t snmp_table_get_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance)
+{
+ snmp_err_t ret = SNMP_ERR_NOSUCHINSTANCE;
+ const struct snmp_table_node* table_node = (const struct snmp_table_node*)(const void*)instance->node;
+
+ LWIP_UNUSED_ARG(root_oid);
+ LWIP_UNUSED_ARG(root_oid_len);
+
+ /* check min. length (fixed row entry definition, column, row instance oid with at least one entry */
+ /* fixed row entry always has oid 1 */
+ if ((instance->instance_oid.len >= 3) && (instance->instance_oid.id[0] == 1)) {
+ /* search column */
+ const struct snmp_table_col_def* col_def = table_node->columns;
+ u16_t i = table_node->column_count;
+ while (i > 0) {
+ if (col_def->index == instance->instance_oid.id[1]) {
+ break;
+ }
+
+ col_def++;
+ i--;
+ }
+
+ if (i > 0) {
+ /* everything may be overwritten by get_cell_instance_method() in order to implement special handling for single columns/cells */
+ instance->asn1_type = col_def->asn1_type;
+ instance->access = col_def->access;
+ instance->get_value = table_node->get_value;
+ instance->set_test = table_node->set_test;
+ instance->set_value = table_node->set_value;
+
+ ret = table_node->get_cell_instance(
+ &(instance->instance_oid.id[1]),
+ &(instance->instance_oid.id[2]),
+ instance->instance_oid.len-2,
+ instance);
+ }
+ }
+
+ return ret;
+}
+
+snmp_err_t snmp_table_get_next_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance)
+{
+ const struct snmp_table_node* table_node = (const struct snmp_table_node*)(const void*)instance->node;
+ const struct snmp_table_col_def* col_def;
+ struct snmp_obj_id row_oid;
+ u32_t column = 0;
+ snmp_err_t result;
+
+ LWIP_UNUSED_ARG(root_oid);
+ LWIP_UNUSED_ARG(root_oid_len);
+
+ /* check that first part of id is 0 or 1, referencing fixed row entry */
+ if ((instance->instance_oid.len > 0) && (instance->instance_oid.id[0] > 1)) {
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+ if (instance->instance_oid.len > 1) {
+ column = instance->instance_oid.id[1];
+ }
+ if (instance->instance_oid.len > 2) {
+ snmp_oid_assign(&row_oid, &(instance->instance_oid.id[2]), instance->instance_oid.len - 2);
+ } else {
+ row_oid.len = 0;
+ }
+
+ instance->get_value = table_node->get_value;
+ instance->set_test = table_node->set_test;
+ instance->set_value = table_node->set_value;
+
+ /* resolve column and value */
+ do {
+ u16_t i;
+ const struct snmp_table_col_def* next_col_def = NULL;
+ col_def = table_node->columns;
+
+ for (i = 0; i < table_node->column_count; i++) {
+ if (col_def->index == column) {
+ next_col_def = col_def;
+ break;
+ } else if ((col_def->index > column) && ((next_col_def == NULL) || (col_def->index < next_col_def->index))) {
+ next_col_def = col_def;
+ }
+ col_def++;
+ }
+
+ if (next_col_def == NULL) {
+ /* no further column found */
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ instance->asn1_type = next_col_def->asn1_type;
+ instance->access = next_col_def->access;
+
+ result = table_node->get_next_cell_instance(
+ &next_col_def->index,
+ &row_oid,
+ instance);
+
+ if (result == SNMP_ERR_NOERROR) {
+ col_def = next_col_def;
+ break;
+ }
+
+ row_oid.len = 0; /* reset row_oid because we switch to next column and start with the first entry there */
+ column = next_col_def->index + 1;
+ } while (1);
+
+ /* build resulting oid */
+ instance->instance_oid.len = 2;
+ instance->instance_oid.id[0] = 1;
+ instance->instance_oid.id[1] = col_def->index;
+ snmp_oid_append(&instance->instance_oid, row_oid.id, row_oid.len);
+
+ return SNMP_ERR_NOERROR;
+}
+
+
+snmp_err_t snmp_table_simple_get_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance)
+{
+ snmp_err_t ret = SNMP_ERR_NOSUCHINSTANCE;
+ const struct snmp_table_simple_node* table_node = (const struct snmp_table_simple_node*)(const void*)instance->node;
+
+ LWIP_UNUSED_ARG(root_oid);
+ LWIP_UNUSED_ARG(root_oid_len);
+
+ /* check min. length (fixed row entry definition, column, row instance oid with at least one entry */
+ /* fixed row entry always has oid 1 */
+ if ((instance->instance_oid.len >= 3) && (instance->instance_oid.id[0] == 1)) {
+ ret = table_node->get_cell_value(
+ &(instance->instance_oid.id[1]),
+ &(instance->instance_oid.id[2]),
+ instance->instance_oid.len-2,
+ &instance->reference,
+ &instance->reference_len);
+
+ if (ret == SNMP_ERR_NOERROR) {
+ /* search column */
+ const struct snmp_table_simple_col_def* col_def = table_node->columns;
+ u32_t i = table_node->column_count;
+ while (i > 0) {
+ if (col_def->index == instance->instance_oid.id[1]) {
+ break;
+ }
+
+ col_def++;
+ i--;
+ }
+
+ if (i > 0) {
+ instance->asn1_type = col_def->asn1_type;
+ instance->access = SNMP_NODE_INSTANCE_READ_ONLY;
+ instance->set_test = NULL;
+ instance->set_value = NULL;
+
+ switch (col_def->data_type) {
+ case SNMP_VARIANT_VALUE_TYPE_U32:
+ instance->get_value = snmp_table_extract_value_from_u32ref;
+ break;
+ case SNMP_VARIANT_VALUE_TYPE_S32:
+ instance->get_value = snmp_table_extract_value_from_s32ref;
+ break;
+ case SNMP_VARIANT_VALUE_TYPE_PTR: /* fall through */
+ case SNMP_VARIANT_VALUE_TYPE_CONST_PTR:
+ instance->get_value = snmp_table_extract_value_from_refconstptr;
+ break;
+ default:
+ LWIP_DEBUGF(SNMP_DEBUG, ("snmp_table_simple_get_instance(): unknown column data_type: %d\n", col_def->data_type));
+ return SNMP_ERR_GENERROR;
+ }
+
+ ret = SNMP_ERR_NOERROR;
+ } else {
+ ret = SNMP_ERR_NOSUCHINSTANCE;
+ }
+ }
+ }
+
+ return ret;
+}
+
+snmp_err_t snmp_table_simple_get_next_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance)
+{
+ const struct snmp_table_simple_node* table_node = (const struct snmp_table_simple_node*)(const void*)instance->node;
+ const struct snmp_table_simple_col_def* col_def;
+ struct snmp_obj_id row_oid;
+ u32_t column = 0;
+ snmp_err_t result;
+
+ LWIP_UNUSED_ARG(root_oid);
+ LWIP_UNUSED_ARG(root_oid_len);
+
+ /* check that first part of id is 0 or 1, referencing fixed row entry */
+ if ((instance->instance_oid.len > 0) && (instance->instance_oid.id[0] > 1)) {
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+ if (instance->instance_oid.len > 1) {
+ column = instance->instance_oid.id[1];
+ }
+ if (instance->instance_oid.len > 2) {
+ snmp_oid_assign(&row_oid, &(instance->instance_oid.id[2]), instance->instance_oid.len - 2);
+ } else {
+ row_oid.len = 0;
+ }
+
+ /* resolve column and value */
+ do {
+ u32_t i;
+ const struct snmp_table_simple_col_def* next_col_def = NULL;
+ col_def = table_node->columns;
+
+ for (i = 0; i < table_node->column_count; i++) {
+ if (col_def->index == column) {
+ next_col_def = col_def;
+ break;
+ } else if ((col_def->index > column) && ((next_col_def == NULL) ||
+ (col_def->index < next_col_def->index))) {
+ next_col_def = col_def;
+ }
+ col_def++;
+ }
+
+ if (next_col_def == NULL) {
+ /* no further column found */
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ result = table_node->get_next_cell_instance_and_value(
+ &next_col_def->index,
+ &row_oid,
+ &instance->reference,
+ &instance->reference_len);
+
+ if (result == SNMP_ERR_NOERROR) {
+ col_def = next_col_def;
+ break;
+ }
+
+ row_oid.len = 0; /* reset row_oid because we switch to next column and start with the first entry there */
+ column = next_col_def->index + 1;
+ }
+ while (1);
+
+ instance->asn1_type = col_def->asn1_type;
+ instance->access = SNMP_NODE_INSTANCE_READ_ONLY;
+ instance->set_test = NULL;
+ instance->set_value = NULL;
+
+ switch (col_def->data_type) {
+ case SNMP_VARIANT_VALUE_TYPE_U32:
+ instance->get_value = snmp_table_extract_value_from_u32ref;
+ break;
+ case SNMP_VARIANT_VALUE_TYPE_S32:
+ instance->get_value = snmp_table_extract_value_from_s32ref;
+ break;
+ case SNMP_VARIANT_VALUE_TYPE_PTR: /* fall through */
+ case SNMP_VARIANT_VALUE_TYPE_CONST_PTR:
+ instance->get_value = snmp_table_extract_value_from_refconstptr;
+ break;
+ default:
+ LWIP_DEBUGF(SNMP_DEBUG, ("snmp_table_simple_get_instance(): unknown column data_type: %d\n", col_def->data_type));
+ return SNMP_ERR_GENERROR;
+ }
+
+ /* build resulting oid */
+ instance->instance_oid.len = 2;
+ instance->instance_oid.id[0] = 1;
+ instance->instance_oid.id[1] = col_def->index;
+ snmp_oid_append(&instance->instance_oid, row_oid.id, row_oid.len);
+
+ return SNMP_ERR_NOERROR;
+}
+
+
+s16_t
+snmp_table_extract_value_from_s32ref(struct snmp_node_instance* instance, void* value)
+{
+ s32_t *dst = (s32_t*)value;
+ *dst = instance->reference.s32;
+ return sizeof(*dst);
+}
+
+s16_t
+snmp_table_extract_value_from_u32ref(struct snmp_node_instance* instance, void* value)
+{
+ u32_t *dst = (u32_t*)value;
+ *dst = instance->reference.u32;
+ return sizeof(*dst);
+}
+
+s16_t
+snmp_table_extract_value_from_refconstptr(struct snmp_node_instance* instance, void* value)
+{
+ MEMCPY(value, instance->reference.const_ptr, instance->reference_len);
+ return (u16_t)instance->reference_len;
+}
+
+#endif /* LWIP_SNMP */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_threadsync.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_threadsync.c
new file mode 100644
index 0000000..204f265
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_threadsync.c
@@ -0,0 +1,219 @@
+/**
+ * @file
+ * SNMP thread synchronization implementation.
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Dirk Ziegelmeier <dziegel@gmx.de>
+ */
+
+#include "lwip/apps/snmp_opts.h"
+
+#if LWIP_SNMP && (NO_SYS == 0) /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/apps/snmp_threadsync.h"
+#include "lwip/apps/snmp_core.h"
+#include "lwip/sys.h"
+#include <string.h>
+
+static void
+call_synced_function(struct threadsync_data *call_data, snmp_threadsync_called_fn fn)
+{
+ sys_mutex_lock(&call_data->threadsync_node->instance->sem_usage_mutex);
+ call_data->threadsync_node->instance->sync_fn(fn, call_data);
+ sys_sem_wait(&call_data->threadsync_node->instance->sem);
+ sys_mutex_unlock(&call_data->threadsync_node->instance->sem_usage_mutex);
+}
+
+static void
+threadsync_get_value_synced(void *ctx)
+{
+ struct threadsync_data *call_data = (struct threadsync_data*)ctx;
+
+ call_data->retval.s16 = call_data->proxy_instance.get_value(&call_data->proxy_instance, call_data->arg1.value);
+
+ sys_sem_signal(&call_data->threadsync_node->instance->sem);
+}
+
+static s16_t
+threadsync_get_value(struct snmp_node_instance* instance, void* value)
+{
+ struct threadsync_data *call_data = (struct threadsync_data*)instance->reference.ptr;
+
+ call_data->arg1.value = value;
+ call_synced_function(call_data, threadsync_get_value_synced);
+
+ return call_data->retval.s16;
+}
+
+static void
+threadsync_set_test_synced(void *ctx)
+{
+ struct threadsync_data *call_data = (struct threadsync_data*)ctx;
+
+ call_data->retval.err = call_data->proxy_instance.set_test(&call_data->proxy_instance, call_data->arg2.len, call_data->arg1.value);
+
+ sys_sem_signal(&call_data->threadsync_node->instance->sem);
+}
+
+static snmp_err_t
+threadsync_set_test(struct snmp_node_instance* instance, u16_t len, void *value)
+{
+ struct threadsync_data *call_data = (struct threadsync_data*)instance->reference.ptr;
+
+ call_data->arg1.value = value;
+ call_data->arg2.len = len;
+ call_synced_function(call_data, threadsync_set_test_synced);
+
+ return call_data->retval.err;
+}
+
+static void
+threadsync_set_value_synced(void *ctx)
+{
+ struct threadsync_data *call_data = (struct threadsync_data*)ctx;
+
+ call_data->retval.err = call_data->proxy_instance.set_value(&call_data->proxy_instance, call_data->arg2.len, call_data->arg1.value);
+
+ sys_sem_signal(&call_data->threadsync_node->instance->sem);
+}
+
+static snmp_err_t
+threadsync_set_value(struct snmp_node_instance* instance, u16_t len, void *value)
+{
+ struct threadsync_data *call_data = (struct threadsync_data*)instance->reference.ptr;
+
+ call_data->arg1.value = value;
+ call_data->arg2.len = len;
+ call_synced_function(call_data, threadsync_set_value_synced);
+
+ return call_data->retval.err;
+}
+
+static void
+threadsync_release_instance_synced(void* ctx)
+{
+ struct threadsync_data *call_data = (struct threadsync_data*)ctx;
+
+ call_data->proxy_instance.release_instance(&call_data->proxy_instance);
+
+ sys_sem_signal(&call_data->threadsync_node->instance->sem);
+}
+
+static void
+threadsync_release_instance(struct snmp_node_instance *instance)
+{
+ struct threadsync_data *call_data = (struct threadsync_data*)instance->reference.ptr;
+
+ if (call_data->proxy_instance.release_instance != NULL) {
+ call_synced_function(call_data, threadsync_release_instance_synced);
+ }
+}
+
+static void
+get_instance_synced(void* ctx)
+{
+ struct threadsync_data *call_data = (struct threadsync_data*)ctx;
+ const struct snmp_leaf_node *leaf = (const struct snmp_leaf_node*)(const void*)call_data->proxy_instance.node;
+
+ call_data->retval.err = leaf->get_instance(call_data->arg1.root_oid, call_data->arg2.root_oid_len, &call_data->proxy_instance);
+
+ sys_sem_signal(&call_data->threadsync_node->instance->sem);
+}
+
+static void
+get_next_instance_synced(void* ctx)
+{
+ struct threadsync_data *call_data = (struct threadsync_data*)ctx;
+ const struct snmp_leaf_node *leaf = (const struct snmp_leaf_node*)(const void*)call_data->proxy_instance.node;
+
+ call_data->retval.err = leaf->get_next_instance(call_data->arg1.root_oid, call_data->arg2.root_oid_len, &call_data->proxy_instance);
+
+ sys_sem_signal(&call_data->threadsync_node->instance->sem);
+}
+
+static snmp_err_t
+do_sync(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance, snmp_threadsync_called_fn fn)
+{
+ const struct snmp_threadsync_node *threadsync_node = (const struct snmp_threadsync_node*)(const void*)instance->node;
+ struct threadsync_data *call_data = &threadsync_node->instance->data;
+
+ if (threadsync_node->node.node.oid != threadsync_node->target->node.oid) {
+ LWIP_DEBUGF(SNMP_DEBUG, ("Sync node OID does not match target node OID"));
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ memset(&call_data->proxy_instance, 0, sizeof(call_data->proxy_instance));
+
+ instance->reference.ptr = call_data;
+ snmp_oid_assign(&call_data->proxy_instance.instance_oid, instance->instance_oid.id, instance->instance_oid.len);
+
+ call_data->proxy_instance.node = &threadsync_node->target->node;
+ call_data->threadsync_node = threadsync_node;
+
+ call_data->arg1.root_oid = root_oid;
+ call_data->arg2.root_oid_len = root_oid_len;
+ call_synced_function(call_data, fn);
+
+ if (call_data->retval.err == SNMP_ERR_NOERROR) {
+ instance->access = call_data->proxy_instance.access;
+ instance->asn1_type = call_data->proxy_instance.asn1_type;
+ instance->release_instance = threadsync_release_instance;
+ instance->get_value = (call_data->proxy_instance.get_value != NULL)? threadsync_get_value : NULL;
+ instance->set_value = (call_data->proxy_instance.set_value != NULL)? threadsync_set_value : NULL;
+ instance->set_test = (call_data->proxy_instance.set_test != NULL)? threadsync_set_test : NULL;
+ snmp_oid_assign(&instance->instance_oid, call_data->proxy_instance.instance_oid.id, call_data->proxy_instance.instance_oid.len);
+ }
+
+ return call_data->retval.err;
+}
+
+snmp_err_t
+snmp_threadsync_get_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance)
+{
+ return do_sync(root_oid, root_oid_len, instance, get_instance_synced);
+}
+
+snmp_err_t
+snmp_threadsync_get_next_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance)
+{
+ return do_sync(root_oid, root_oid_len, instance, get_next_instance_synced);
+}
+
+/** Initializes thread synchronization instance */
+void snmp_threadsync_init(struct snmp_threadsync_instance *instance, snmp_threadsync_synchronizer_fn sync_fn)
+{
+ err_t err = sys_mutex_new(&instance->sem_usage_mutex);
+ LWIP_ASSERT("Failed to set up mutex", err == ERR_OK);
+ err = sys_sem_new(&instance->sem, 0);
+ LWIP_UNUSED_ARG(err); /* in case of LWIP_NOASSERT */
+ LWIP_ASSERT("Failed to set up semaphore", err == ERR_OK);
+ instance->sync_fn = sync_fn;
+}
+
+#endif /* LWIP_SNMP */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_traps.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_traps.c
new file mode 100644
index 0000000..0d2df64
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_traps.c
@@ -0,0 +1,445 @@
+/**
+ * @file
+ * SNMPv1 traps implementation.
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Martin Hentschel
+ * Christiaan Simons <christiaan.simons@axon.tv>
+ *
+ */
+
+#include "lwip/apps/snmp_opts.h"
+
+#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
+
+#include <string.h>
+
+#include "lwip/snmp.h"
+#include "lwip/sys.h"
+#include "lwip/apps/snmp.h"
+#include "lwip/apps/snmp_core.h"
+#include "snmp_msg.h"
+#include "snmp_asn1.h"
+#include "snmp_core_priv.h"
+
+struct snmp_msg_trap
+{
+ /* source enterprise ID (sysObjectID) */
+ const struct snmp_obj_id *enterprise;
+ /* source IP address, raw network order format */
+ ip_addr_t sip;
+ /* generic trap code */
+ u32_t gen_trap;
+ /* specific trap code */
+ u32_t spc_trap;
+ /* timestamp */
+ u32_t ts;
+ /* snmp_version */
+ u32_t snmp_version;
+
+ /* output trap lengths used in ASN encoding */
+ /* encoding pdu length */
+ u16_t pdulen;
+ /* encoding community length */
+ u16_t comlen;
+ /* encoding sequence length */
+ u16_t seqlen;
+ /* encoding varbinds sequence length */
+ u16_t vbseqlen;
+};
+
+static u16_t snmp_trap_varbind_sum(struct snmp_msg_trap *trap, struct snmp_varbind *varbinds);
+static u16_t snmp_trap_header_sum(struct snmp_msg_trap *trap, u16_t vb_len);
+static void snmp_trap_header_enc(struct snmp_msg_trap *trap, struct snmp_pbuf_stream *pbuf_stream);
+static void snmp_trap_varbind_enc(struct snmp_msg_trap *trap, struct snmp_pbuf_stream *pbuf_stream, struct snmp_varbind *varbinds);
+
+/** Agent community string for sending traps */
+extern const char *snmp_community_trap;
+
+void* snmp_traps_handle;
+
+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;
+};
+static struct snmp_trap_dst trap_dst[SNMP_TRAP_DESTINATIONS];
+
+static u8_t snmp_auth_traps_enabled = 0;
+
+/**
+ * @ingroup snmp_traps
+ * 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;
+ }
+}
+
+/**
+ * @ingroup snmp_traps
+ * 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, const ip_addr_t *dst)
+{
+ if (dst_idx < SNMP_TRAP_DESTINATIONS) {
+ ip_addr_set(&trap_dst[dst_idx].dip, dst);
+ }
+}
+
+/**
+ * @ingroup snmp_traps
+ * Enable/disable authentication traps
+ */
+void
+snmp_set_auth_traps_enabled(u8_t enable)
+{
+ snmp_auth_traps_enabled = enable;
+}
+
+/**
+ * @ingroup snmp_traps
+ * Get authentication traps enabled state
+ */
+u8_t
+snmp_get_auth_traps_enabled(void)
+{
+ return snmp_auth_traps_enabled;
+}
+
+
+/**
+ * @ingroup snmp_traps
+ * Sends a generic or enterprise specific trap message.
+ *
+ * @param eoid points to enterprise object identifier
+ * @param generic_trap is the trap code
+ * @param specific_trap used for enterprise traps when generic_trap == 6
+ * @param varbinds linked list of varbinds to be sent
+ * @return ERR_OK when success, ERR_MEM if we're out of memory
+ *
+ * @note the use of the enterprise 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(const struct snmp_obj_id* eoid, s32_t generic_trap, s32_t specific_trap, struct snmp_varbind *varbinds)
+{
+ struct snmp_msg_trap trap_msg;
+ struct snmp_trap_dst *td;
+ struct pbuf *p;
+ u16_t i, tot_len;
+ err_t err = ERR_OK;
+
+ trap_msg.snmp_version = 0;
+
+ for (i = 0, td = &trap_dst[0]; i < SNMP_TRAP_DESTINATIONS; i++, td++) {
+ if ((td->enable != 0) && !ip_addr_isany(&td->dip)) {
+ /* lookup current source address for this dst */
+ if (snmp_get_local_ip_for_dst(snmp_traps_handle, &td->dip, &trap_msg.sip)) {
+ if (eoid == NULL) {
+ trap_msg.enterprise = snmp_get_device_enterprise_oid();
+ } else {
+ trap_msg.enterprise = eoid;
+ }
+
+ trap_msg.gen_trap = generic_trap;
+ if (generic_trap == SNMP_GENTRAP_ENTERPRISE_SPECIFIC) {
+ trap_msg.spc_trap = specific_trap;
+ } else {
+ trap_msg.spc_trap = 0;
+ }
+
+ MIB2_COPY_SYSUPTIME_TO(&trap_msg.ts);
+
+ /* pass 0, calculate length fields */
+ tot_len = snmp_trap_varbind_sum(&trap_msg, varbinds);
+ tot_len = snmp_trap_header_sum(&trap_msg, tot_len);
+
+ /* allocate pbuf(s) */
+ p = pbuf_alloc(PBUF_TRANSPORT, tot_len, PBUF_RAM);
+ if (p != NULL) {
+ struct snmp_pbuf_stream pbuf_stream;
+ snmp_pbuf_stream_init(&pbuf_stream, p, 0, tot_len);
+
+ /* pass 1, encode packet ino the pbuf(s) */
+ snmp_trap_header_enc(&trap_msg, &pbuf_stream);
+ snmp_trap_varbind_enc(&trap_msg, &pbuf_stream, varbinds);
+
+ snmp_stats.outtraps++;
+ snmp_stats.outpkts++;
+
+ /** send to the TRAP destination */
+ snmp_sendto(snmp_traps_handle, p, &td->dip, SNMP_TRAP_PORT);
+ pbuf_free(p);
+ } else {
+ err = ERR_MEM;
+ }
+ } else {
+ /* routing error */
+ err = ERR_RTE;
+ }
+ }
+ }
+ return err;
+}
+
+/**
+ * @ingroup snmp_traps
+ * Send generic SNMP trap
+ */
+err_t
+snmp_send_trap_generic(s32_t generic_trap)
+{
+ static const struct snmp_obj_id oid = { 7, { 1, 3, 6, 1, 2, 1, 11 } };
+ return snmp_send_trap(&oid, generic_trap, 0, NULL);
+}
+
+/**
+ * @ingroup snmp_traps
+ * Send specific SNMP trap with variable bindings
+ */
+err_t
+snmp_send_trap_specific(s32_t specific_trap, struct snmp_varbind *varbinds)
+{
+ return snmp_send_trap(NULL, SNMP_GENTRAP_ENTERPRISE_SPECIFIC, specific_trap, varbinds);
+}
+
+/**
+ * @ingroup snmp_traps
+ * Send coldstart trap
+ */
+void
+snmp_coldstart_trap(void)
+{
+ snmp_send_trap_generic(SNMP_GENTRAP_COLDSTART);
+}
+
+/**
+ * @ingroup snmp_traps
+ * Send authentication failure trap (used internally by agent)
+ */
+void
+snmp_authfail_trap(void)
+{
+ if (snmp_auth_traps_enabled != 0) {
+ snmp_send_trap_generic(SNMP_GENTRAP_AUTH_FAILURE);
+ }
+}
+
+static u16_t
+snmp_trap_varbind_sum(struct snmp_msg_trap *trap, struct snmp_varbind *varbinds)
+{
+ struct snmp_varbind *varbind;
+ u16_t tot_len;
+ u8_t tot_len_len;
+
+ tot_len = 0;
+ varbind = varbinds;
+ while (varbind != NULL) {
+ struct snmp_varbind_len len;
+
+ if (snmp_varbind_length(varbind, &len) == ERR_OK) {
+ tot_len += 1 + len.vb_len_len + len.vb_value_len;
+ }
+
+ varbind = varbind->next;
+ }
+
+ trap->vbseqlen = tot_len;
+ snmp_asn1_enc_length_cnt(trap->vbseqlen, &tot_len_len);
+ tot_len += 1 + tot_len_len;
+
+ return tot_len;
+}
+
+/**
+ * Sums trap header field lengths from tail to head and
+ * returns trap_header_lengths for second encoding pass.
+ *
+ * @param trap Trap message
+ * @param vb_len varbind-list length
+ * @return the required length for encoding the trap header
+ */
+static u16_t
+snmp_trap_header_sum(struct snmp_msg_trap *trap, u16_t vb_len)
+{
+ u16_t tot_len;
+ u16_t len;
+ u8_t lenlen;
+
+ tot_len = vb_len;
+
+ snmp_asn1_enc_u32t_cnt(trap->ts, &len);
+ snmp_asn1_enc_length_cnt(len, &lenlen);
+ tot_len += 1 + len + lenlen;
+
+ snmp_asn1_enc_s32t_cnt(trap->spc_trap, &len);
+ snmp_asn1_enc_length_cnt(len, &lenlen);
+ tot_len += 1 + len + lenlen;
+
+ snmp_asn1_enc_s32t_cnt(trap->gen_trap, &len);
+ snmp_asn1_enc_length_cnt(len, &lenlen);
+ tot_len += 1 + len + lenlen;
+
+ if (IP_IS_V6_VAL(trap->sip)) {
+#if LWIP_IPV6
+ len = sizeof(ip_2_ip6(&trap->sip)->addr);
+#endif
+ } else {
+#if LWIP_IPV4
+ len = sizeof(ip_2_ip4(&trap->sip)->addr);
+#endif
+ }
+ snmp_asn1_enc_length_cnt(len, &lenlen);
+ tot_len += 1 + len + lenlen;
+
+ snmp_asn1_enc_oid_cnt(trap->enterprise->id, trap->enterprise->len, &len);
+ snmp_asn1_enc_length_cnt(len, &lenlen);
+ tot_len += 1 + len + lenlen;
+
+ trap->pdulen = tot_len;
+ snmp_asn1_enc_length_cnt(trap->pdulen, &lenlen);
+ tot_len += 1 + lenlen;
+
+ trap->comlen = (u16_t)LWIP_MIN(strlen(snmp_community_trap), 0xFFFF);
+ snmp_asn1_enc_length_cnt(trap->comlen, &lenlen);
+ tot_len += 1 + lenlen + trap->comlen;
+
+ snmp_asn1_enc_s32t_cnt(trap->snmp_version, &len);
+ snmp_asn1_enc_length_cnt(len, &lenlen);
+ tot_len += 1 + len + lenlen;
+
+ trap->seqlen = tot_len;
+ snmp_asn1_enc_length_cnt(trap->seqlen, &lenlen);
+ tot_len += 1 + lenlen;
+
+ return tot_len;
+}
+
+static void
+snmp_trap_varbind_enc(struct snmp_msg_trap *trap, struct snmp_pbuf_stream *pbuf_stream, struct snmp_varbind *varbinds)
+{
+ struct snmp_asn1_tlv tlv;
+ struct snmp_varbind *varbind;
+
+ varbind = varbinds;
+
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 0, trap->vbseqlen);
+ snmp_ans1_enc_tlv(pbuf_stream, &tlv);
+
+ while (varbind != NULL) {
+ snmp_append_outbound_varbind(pbuf_stream, varbind);
+
+ varbind = varbind->next;
+ }
+}
+
+/**
+ * Encodes trap header from head to tail.
+ */
+static void
+snmp_trap_header_enc(struct snmp_msg_trap *trap, struct snmp_pbuf_stream *pbuf_stream)
+{
+ struct snmp_asn1_tlv tlv;
+
+ /* 'Message' sequence */
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 0, trap->seqlen);
+ snmp_ans1_enc_tlv(pbuf_stream, &tlv);
+
+ /* version */
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 0);
+ snmp_asn1_enc_s32t_cnt(trap->snmp_version, &tlv.value_len);
+ snmp_ans1_enc_tlv(pbuf_stream, &tlv);
+ snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, trap->snmp_version);
+
+ /* community */
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 0, trap->comlen);
+ snmp_ans1_enc_tlv(pbuf_stream, &tlv);
+ snmp_asn1_enc_raw(pbuf_stream, (const u8_t *)snmp_community_trap, trap->comlen);
+
+ /* 'PDU' sequence */
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, (SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_CONTEXT_PDU_TRAP), 0, trap->pdulen);
+ snmp_ans1_enc_tlv(pbuf_stream, &tlv);
+
+ /* object ID */
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OBJECT_ID, 0, 0);
+ snmp_asn1_enc_oid_cnt(trap->enterprise->id, trap->enterprise->len, &tlv.value_len);
+ snmp_ans1_enc_tlv(pbuf_stream, &tlv);
+ snmp_asn1_enc_oid(pbuf_stream, trap->enterprise->id, trap->enterprise->len);
+
+ /* IP addr */
+ if (IP_IS_V6_VAL(trap->sip)) {
+#if LWIP_IPV6
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_IPADDR, 0, sizeof(ip_2_ip6(&trap->sip)->addr));
+ snmp_ans1_enc_tlv(pbuf_stream, &tlv);
+ snmp_asn1_enc_raw(pbuf_stream, (const u8_t *)&ip_2_ip6(&trap->sip)->addr, sizeof(ip_2_ip6(&trap->sip)->addr));
+#endif
+ } else {
+#if LWIP_IPV4
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_IPADDR, 0, sizeof(ip_2_ip4(&trap->sip)->addr));
+ snmp_ans1_enc_tlv(pbuf_stream, &tlv);
+ snmp_asn1_enc_raw(pbuf_stream, (const u8_t *)&ip_2_ip4(&trap->sip)->addr, sizeof(ip_2_ip4(&trap->sip)->addr));
+#endif
+ }
+
+ /* trap length */
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 0);
+ snmp_asn1_enc_s32t_cnt(trap->gen_trap, &tlv.value_len);
+ snmp_ans1_enc_tlv(pbuf_stream, &tlv);
+ snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, trap->gen_trap);
+
+ /* specific trap */
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 0);
+ snmp_asn1_enc_s32t_cnt(trap->spc_trap, &tlv.value_len);
+ snmp_ans1_enc_tlv(pbuf_stream, &tlv);
+ snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, trap->spc_trap);
+
+ /* timestamp */
+ SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_TIMETICKS, 0, 0);
+ snmp_asn1_enc_s32t_cnt(trap->ts, &tlv.value_len);
+ snmp_ans1_enc_tlv(pbuf_stream, &tlv);
+ snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, trap->ts);
+}
+
+#endif /* LWIP_SNMP */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmpv3.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmpv3.c
new file mode 100644
index 0000000..69fb3a0
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmpv3.c
@@ -0,0 +1,136 @@
+/**
+ * @file
+ * Additional SNMPv3 functionality RFC3414 and RFC3826.
+ */
+
+/*
+ * Copyright (c) 2016 Elias Oenal.
+ * 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: Elias Oenal <lwip@eliasoenal.com>
+ */
+
+#include "snmpv3_priv.h"
+#include "lwip/apps/snmpv3.h"
+#include "lwip/sys.h"
+#include <string.h>
+
+#if LWIP_SNMP && LWIP_SNMP_V3
+
+#ifdef LWIP_SNMPV3_INCLUDE_ENGINE
+#include LWIP_SNMPV3_INCLUDE_ENGINE
+#endif
+
+#define SNMP_MAX_TIME_BOOT 2147483647UL
+
+/** Call this if engine has been changed. Has to reset boots, see below */
+void
+snmpv3_engine_id_changed(void)
+{
+ snmpv3_set_engine_boots(0);
+}
+
+/** According to RFC3414 2.2.2.
+ *
+ * The number of times that the SNMP engine has
+ * (re-)initialized itself since snmpEngineID
+ * was last configured.
+ */
+u32_t
+snmpv3_get_engine_boots_internal(void)
+{
+ if (snmpv3_get_engine_boots() == 0 ||
+ snmpv3_get_engine_boots() < SNMP_MAX_TIME_BOOT) {
+ return snmpv3_get_engine_boots();
+ }
+
+ snmpv3_set_engine_boots(SNMP_MAX_TIME_BOOT);
+ return snmpv3_get_engine_boots();
+}
+
+/** RFC3414 2.2.2.
+ *
+ * Once the timer reaches 2147483647 it gets reset to zero and the
+ * engine boot ups get incremented.
+ */
+u32_t
+snmpv3_get_engine_time_internal(void)
+{
+ if (snmpv3_get_engine_time() >= SNMP_MAX_TIME_BOOT) {
+ snmpv3_reset_engine_time();
+
+ if (snmpv3_get_engine_boots() < SNMP_MAX_TIME_BOOT - 1) {
+ snmpv3_set_engine_boots(snmpv3_get_engine_boots() + 1);
+ } else {
+ snmpv3_set_engine_boots(SNMP_MAX_TIME_BOOT);
+ }
+ }
+
+ return snmpv3_get_engine_time();
+}
+
+#if LWIP_SNMP_V3_CRYPTO
+
+/* This function ignores the byte order suggestion in RFC3414
+ * since it simply doesn't influence the effectiveness of an IV.
+ *
+ * Implementing RFC3826 priv param algorithm if LWIP_RAND is available.
+ *
+ * @todo: This is a potential thread safety issue.
+ */
+err_t
+snmpv3_build_priv_param(u8_t* priv_param)
+{
+#ifdef LWIP_RAND /* Based on RFC3826 */
+ static u8_t init;
+ static u32_t priv1, priv2;
+
+ /* Lazy initialisation */
+ if (init == 0) {
+ init = 1;
+ priv1 = LWIP_RAND();
+ priv2 = LWIP_RAND();
+ }
+
+ SMEMCPY(&priv_param[0], &priv1, sizeof(priv1));
+ SMEMCPY(&priv_param[4], &priv2, sizeof(priv2));
+
+ /* Emulate 64bit increment */
+ priv1++;
+ if (!priv1) { /* Overflow */
+ priv2++;
+ }
+#else /* Based on RFC3414 */
+ static u32_t ctr;
+ u32_t boots = LWIP_SNMPV3_GET_ENGINE_BOOTS();
+ SMEMCPY(&priv_param[0], &boots, 4);
+ SMEMCPY(&priv_param[4], &ctr, 4);
+ ctr++;
+#endif
+ return ERR_OK;
+}
+#endif /* LWIP_SNMP_V3_CRYPTO */
+
+#endif
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmpv3_dummy.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmpv3_dummy.c
new file mode 100644
index 0000000..bdfe844
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmpv3_dummy.c
@@ -0,0 +1,145 @@
+/**
+ * @file
+ * Dummy SNMPv3 functions.
+ */
+
+/*
+ * Copyright (c) 2016 Elias Oenal.
+ * 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: Elias Oenal <lwip@eliasoenal.com>
+ * Dirk Ziegelmeier <dirk@ziegelmeier.net>
+ */
+
+#include "lwip/apps/snmpv3.h"
+#include "snmpv3_priv.h"
+#include <string.h>
+#include "lwip/err.h"
+
+#if LWIP_SNMP && LWIP_SNMP_V3
+
+/**
+ * @param username is a pointer to a string.
+ * @param auth_algo is a pointer to u8_t. The implementation has to set this if user was found.
+ * @param auth_key is a pointer to a pointer to a string. Implementation has to set this if user was found.
+ * @param priv_algo is a pointer to u8_t. The implementation has to set this if user was found.
+ * @param priv_key is a pointer to a pointer to a string. Implementation has to set this if user was found.
+ */
+err_t
+snmpv3_get_user(const char* username, u8_t *auth_algo, u8_t *auth_key, u8_t *priv_algo, u8_t *priv_key)
+{
+ const char* engine_id;
+ u8_t engine_id_len;
+
+ if(strlen(username) == 0) {
+ return ERR_OK;
+ }
+
+ if(memcmp(username, "lwip", 4) != 0) {
+ return ERR_VAL;
+ }
+
+ snmpv3_get_engine_id(&engine_id, &engine_id_len);
+
+ if(auth_key != NULL) {
+ snmpv3_password_to_key_sha((const u8_t*)"maplesyrup", 10,
+ (const u8_t*)engine_id, engine_id_len,
+ auth_key);
+ *auth_algo = SNMP_V3_AUTH_ALGO_SHA;
+ }
+ if(priv_key != NULL) {
+ snmpv3_password_to_key_sha((const u8_t*)"maplesyrup", 10,
+ (const u8_t*)engine_id, engine_id_len,
+ priv_key);
+ *priv_algo = SNMP_V3_PRIV_ALGO_DES;
+ }
+ return ERR_OK;
+}
+
+/**
+ * Get engine ID from persistence
+ * @param id
+ * @param len
+ */
+void
+snmpv3_get_engine_id(const char **id, u8_t *len)
+{
+ *id = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02";
+ *len = 12;
+}
+
+/**
+ * Store engine ID in persistence
+ * @param id
+ * @param len
+ */
+err_t
+snmpv3_set_engine_id(const char *id, u8_t len)
+{
+ LWIP_UNUSED_ARG(id);
+ LWIP_UNUSED_ARG(len);
+ return ERR_OK;
+}
+
+/**
+ * Get engine boots from persistence. Must be increased on each boot.
+ * @return
+ */
+u32_t
+snmpv3_get_engine_boots(void)
+{
+ return 0;
+}
+
+/**
+ * Store engine boots in persistence
+ * @param boots
+ */
+void
+snmpv3_set_engine_boots(u32_t boots)
+{
+ LWIP_UNUSED_ARG(boots);
+}
+
+/**
+ * RFC3414 2.2.2.
+ * Once the timer reaches 2147483647 it gets reset to zero and the
+ * engine boot ups get incremented.
+ */
+u32_t
+snmpv3_get_engine_time(void)
+{
+ return 0;
+}
+
+/**
+ * Reset current engine time to 0
+ */
+void
+snmpv3_reset_engine_time(void)
+{
+}
+
+#endif /* LWIP_SNMP && LWIP_SNMP_V3 */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmpv3_mbedtls.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmpv3_mbedtls.c
new file mode 100644
index 0000000..0b1eefb
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmpv3_mbedtls.c
@@ -0,0 +1,331 @@
+/**
+ * @file
+ * SNMPv3 crypto/auth functions implemented for ARM mbedtls.
+ */
+
+/*
+ * Copyright (c) 2016 Elias Oenal and Dirk Ziegelmeier.
+ * 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: Elias Oenal <lwip@eliasoenal.com>
+ * Dirk Ziegelmeier <dirk@ziegelmeier.net>
+ */
+
+#include "lwip/apps/snmpv3.h"
+#include "snmpv3_priv.h"
+#include "lwip/arch.h"
+#include "snmp_msg.h"
+#include "lwip/sys.h"
+#include <string.h>
+
+#if LWIP_SNMP && LWIP_SNMP_V3 && LWIP_SNMP_V3_MBEDTLS
+
+#include "mbedtls/md.h"
+#include "mbedtls/cipher.h"
+
+#include "mbedtls/md5.h"
+#include "mbedtls/sha1.h"
+
+err_t
+snmpv3_auth(struct snmp_pbuf_stream* stream, u16_t length,
+ const u8_t* key, u8_t algo, u8_t* hmac_out)
+{
+ u32_t i;
+ u8_t key_len;
+ const mbedtls_md_info_t *md_info;
+ mbedtls_md_context_t ctx;
+ struct snmp_pbuf_stream read_stream;
+ snmp_pbuf_stream_init(&read_stream, stream->pbuf, stream->offset, stream->length);
+
+ if (algo == SNMP_V3_AUTH_ALGO_MD5) {
+ md_info = mbedtls_md_info_from_type(MBEDTLS_MD_MD5);
+ key_len = SNMP_V3_MD5_LEN;
+ } else if (algo == SNMP_V3_AUTH_ALGO_SHA) {
+ md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA1);
+ key_len = SNMP_V3_SHA_LEN;
+ } else {
+ return ERR_ARG;
+ }
+
+ mbedtls_md_init(&ctx);
+ if(mbedtls_md_setup(&ctx, md_info, 1) != 0) {
+ return ERR_ARG;
+ }
+
+ if (mbedtls_md_hmac_starts(&ctx, key, key_len) != 0) {
+ goto free_md;
+ }
+
+ for (i = 0; i < length; i++) {
+ u8_t byte;
+
+ if (snmp_pbuf_stream_read(&read_stream, &byte)) {
+ goto free_md;
+ }
+
+ if (mbedtls_md_hmac_update(&ctx, &byte, 1) != 0) {
+ goto free_md;
+ }
+ }
+
+ if (mbedtls_md_hmac_finish(&ctx, hmac_out) != 0) {
+ goto free_md;
+ }
+
+ mbedtls_md_free(&ctx);
+ return ERR_OK;
+
+free_md:
+ mbedtls_md_free(&ctx);
+ return ERR_ARG;
+}
+
+#if LWIP_SNMP_V3_CRYPTO
+
+err_t
+snmpv3_crypt(struct snmp_pbuf_stream* stream, u16_t length,
+ const u8_t* key, const u8_t* priv_param, const u32_t engine_boots,
+ const u32_t engine_time, u8_t algo, u8_t mode)
+{
+ size_t i;
+ mbedtls_cipher_context_t ctx;
+ const mbedtls_cipher_info_t *cipher_info;
+
+ struct snmp_pbuf_stream read_stream;
+ struct snmp_pbuf_stream write_stream;
+ snmp_pbuf_stream_init(&read_stream, stream->pbuf, stream->offset, stream->length);
+ snmp_pbuf_stream_init(&write_stream, stream->pbuf, stream->offset, stream->length);
+ mbedtls_cipher_init(&ctx);
+
+ if (algo == SNMP_V3_PRIV_ALGO_DES) {
+ u8_t iv_local[8];
+ u8_t out_bytes[8];
+ size_t out_len;
+
+ /* RFC 3414 mandates padding for DES */
+ if ((length & 0x07) != 0) {
+ return ERR_ARG;
+ }
+
+ cipher_info = mbedtls_cipher_info_from_type(MBEDTLS_CIPHER_DES_CBC);
+ if(mbedtls_cipher_setup(&ctx, cipher_info) != 0) {
+ return ERR_ARG;
+ }
+ if(mbedtls_cipher_set_padding_mode(&ctx, MBEDTLS_PADDING_NONE) != 0) {
+ return ERR_ARG;
+ }
+ if(mbedtls_cipher_setkey(&ctx, key, 8*8, (mode == SNMP_V3_PRIV_MODE_ENCRYPT)? MBEDTLS_ENCRYPT : MBEDTLS_DECRYPT) != 0) {
+ goto error;
+ }
+
+ /* Prepare IV */
+ for (i = 0; i < LWIP_ARRAYSIZE(iv_local); i++) {
+ iv_local[i] = priv_param[i] ^ key[i + 8];
+ }
+ if(mbedtls_cipher_set_iv(&ctx, iv_local, LWIP_ARRAYSIZE(iv_local)) != 0) {
+ goto error;
+ }
+
+ for (i = 0; i < length; i += 8) {
+ size_t j;
+ u8_t in_bytes[8];
+ out_len = LWIP_ARRAYSIZE(out_bytes) ;
+
+ for (j = 0; j < LWIP_ARRAYSIZE(in_bytes); j++) {
+ snmp_pbuf_stream_read(&read_stream, &in_bytes[j]);
+ }
+
+ if(mbedtls_cipher_update(&ctx, in_bytes, LWIP_ARRAYSIZE(in_bytes), out_bytes, &out_len) != 0) {
+ goto error;
+ }
+
+ snmp_pbuf_stream_writebuf(&write_stream, out_bytes, out_len);
+ }
+
+ out_len = LWIP_ARRAYSIZE(out_bytes);
+ if(mbedtls_cipher_finish(&ctx, out_bytes, &out_len) != 0) {
+ goto error;
+ }
+ snmp_pbuf_stream_writebuf(&write_stream, out_bytes, out_len);
+ } else if (algo == SNMP_V3_PRIV_ALGO_AES) {
+ u8_t iv_local[16];
+
+ cipher_info = mbedtls_cipher_info_from_type(MBEDTLS_CIPHER_AES_128_CFB128);
+ if(mbedtls_cipher_setup(&ctx, cipher_info) != 0) {
+ return ERR_ARG;
+ }
+ if(mbedtls_cipher_setkey(&ctx, key, 16*8, (mode == SNMP_V3_PRIV_MODE_ENCRYPT)? MBEDTLS_ENCRYPT : MBEDTLS_DECRYPT) != 0) {
+ goto error;
+ }
+
+ /*
+ * IV is the big endian concatenation of boots,
+ * uptime and priv param - see RFC3826.
+ */
+ iv_local[0 + 0] = (engine_boots >> 24) & 0xFF;
+ iv_local[0 + 1] = (engine_boots >> 16) & 0xFF;
+ iv_local[0 + 2] = (engine_boots >> 8) & 0xFF;
+ iv_local[0 + 3] = (engine_boots >> 0) & 0xFF;
+ iv_local[4 + 0] = (engine_time >> 24) & 0xFF;
+ iv_local[4 + 1] = (engine_time >> 16) & 0xFF;
+ iv_local[4 + 2] = (engine_time >> 8) & 0xFF;
+ iv_local[4 + 3] = (engine_time >> 0) & 0xFF;
+ SMEMCPY(iv_local + 8, priv_param, 8);
+ if(mbedtls_cipher_set_iv(&ctx, iv_local, LWIP_ARRAYSIZE(iv_local)) != 0) {
+ goto error;
+ }
+
+ for (i = 0; i < length; i++) {
+ u8_t in_byte;
+ u8_t out_byte;
+ size_t out_len = sizeof(out_byte);
+
+ snmp_pbuf_stream_read(&read_stream, &in_byte);
+ if(mbedtls_cipher_update(&ctx, &in_byte, sizeof(in_byte), &out_byte, &out_len) != 0) {
+ goto error;
+ }
+ snmp_pbuf_stream_write(&write_stream, out_byte);
+ }
+ } else {
+ return ERR_ARG;
+ }
+
+ mbedtls_cipher_free(&ctx);
+ return ERR_OK;
+
+error:
+ mbedtls_cipher_free(&ctx);
+ return ERR_OK;
+}
+
+#endif /* LWIP_SNMP_V3_CRYPTO */
+
+/* A.2.1. Password to Key Sample Code for MD5 */
+void
+snmpv3_password_to_key_md5(
+ const u8_t *password, /* IN */
+ u8_t passwordlen, /* IN */
+ const u8_t *engineID, /* IN - pointer to snmpEngineID */
+ u8_t engineLength,/* IN - length of snmpEngineID */
+ u8_t *key) /* OUT - pointer to caller 16-octet buffer */
+{
+ mbedtls_md5_context MD;
+ u8_t *cp, password_buf[64];
+ u32_t password_index = 0;
+ u8_t i;
+ u32_t count = 0;
+
+ mbedtls_md5_init(&MD); /* initialize MD5 */
+ mbedtls_md5_starts(&MD);
+
+ /**********************************************/
+ /* Use while loop until we've done 1 Megabyte */
+ /**********************************************/
+ while (count < 1048576) {
+ cp = password_buf;
+ for (i = 0; i < 64; i++) {
+ /*************************************************/
+ /* Take the next octet of the password, wrapping */
+ /* to the beginning of the password as necessary.*/
+ /*************************************************/
+ *cp++ = password[password_index++ % passwordlen];
+ }
+ mbedtls_md5_update(&MD, password_buf, 64);
+ count += 64;
+ }
+ mbedtls_md5_finish(&MD, key); /* tell MD5 we're done */
+
+ /*****************************************************/
+ /* Now localize the key with the engineID and pass */
+ /* through MD5 to produce final key */
+ /* May want to ensure that engineLength <= 32, */
+ /* otherwise need to use a buffer larger than 64 */
+ /*****************************************************/
+ SMEMCPY(password_buf, key, 16);
+ MEMCPY(password_buf + 16, engineID, engineLength);
+ SMEMCPY(password_buf + 16 + engineLength, key, 16);
+
+ mbedtls_md5_starts(&MD);
+ mbedtls_md5_update(&MD, password_buf, 32 + engineLength);
+ mbedtls_md5_finish(&MD, key);
+
+ mbedtls_md5_free(&MD);
+ return;
+}
+
+/* A.2.2. Password to Key Sample Code for SHA */
+void
+snmpv3_password_to_key_sha(
+ const u8_t *password, /* IN */
+ u8_t passwordlen, /* IN */
+ const u8_t *engineID, /* IN - pointer to snmpEngineID */
+ u8_t engineLength,/* IN - length of snmpEngineID */
+ u8_t *key) /* OUT - pointer to caller 20-octet buffer */
+{
+ mbedtls_sha1_context SH;
+ u8_t *cp, password_buf[72];
+ u32_t password_index = 0;
+ u8_t i;
+ u32_t count = 0;
+
+ mbedtls_sha1_init(&SH); /* initialize SHA */
+ mbedtls_sha1_starts(&SH);
+
+ /**********************************************/
+ /* Use while loop until we've done 1 Megabyte */
+ /**********************************************/
+ while (count < 1048576) {
+ cp = password_buf;
+ for (i = 0; i < 64; i++) {
+ /*************************************************/
+ /* Take the next octet of the password, wrapping */
+ /* to the beginning of the password as necessary.*/
+ /*************************************************/
+ *cp++ = password[password_index++ % passwordlen];
+ }
+ mbedtls_sha1_update(&SH, password_buf, 64);
+ count += 64;
+ }
+ mbedtls_sha1_finish(&SH, key); /* tell SHA we're done */
+
+ /*****************************************************/
+ /* Now localize the key with the engineID and pass */
+ /* through SHA to produce final key */
+ /* May want to ensure that engineLength <= 32, */
+ /* otherwise need to use a buffer larger than 72 */
+ /*****************************************************/
+ SMEMCPY(password_buf, key, 20);
+ MEMCPY(password_buf + 20, engineID, engineLength);
+ SMEMCPY(password_buf + 20 + engineLength, key, 20);
+
+ mbedtls_sha1_starts(&SH);
+ mbedtls_sha1_update(&SH, password_buf, 40 + engineLength);
+ mbedtls_sha1_finish(&SH, key);
+
+ mbedtls_sha1_free(&SH);
+ return;
+}
+
+#endif /* LWIP_SNMP && LWIP_SNMP_V3 && LWIP_SNMP_V3_MBEDTLS */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmpv3_priv.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmpv3_priv.h
new file mode 100644
index 0000000..b87666d
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmpv3_priv.h
@@ -0,0 +1,66 @@
+/**
+ * @file
+ * Additional SNMPv3 functionality RFC3414 and RFC3826 (internal API, do not use in client code).
+ */
+
+/*
+ * Copyright (c) 2016 Elias Oenal.
+ * 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: Elias Oenal <lwip@eliasoenal.com>
+ */
+
+#ifndef LWIP_HDR_APPS_SNMP_V3_PRIV_H
+#define LWIP_HDR_APPS_SNMP_V3_PRIV_H
+
+#include "lwip/apps/snmp_opts.h"
+
+#if LWIP_SNMP && LWIP_SNMP_V3
+
+#include "snmp_pbuf_stream.h"
+
+/* According to RFC 3411 */
+#define SNMP_V3_MAX_ENGINE_ID_LENGTH 32
+#define SNMP_V3_MAX_USER_LENGTH 32
+
+#define SNMP_V3_MAX_AUTH_PARAM_LENGTH 12
+#define SNMP_V3_MAX_PRIV_PARAM_LENGTH 8
+
+#define SNMP_V3_AUTH_FLAG 0x01
+#define SNMP_V3_PRIV_FLAG 0x02
+
+#define SNMP_V3_MD5_LEN 16
+#define SNMP_V3_SHA_LEN 20
+
+u32_t snmpv3_get_engine_boots_internal(void);
+u32_t snmpv3_get_engine_time_internal(void);
+err_t snmpv3_auth(struct snmp_pbuf_stream* stream, u16_t length, const u8_t* key, u8_t algo, u8_t* hmac_out);
+err_t snmpv3_crypt(struct snmp_pbuf_stream* stream, u16_t length, const u8_t* key,
+ const u8_t* priv_param, const u32_t engine_boots, const u32_t engine_time, u8_t algo, u8_t mode);
+err_t snmpv3_build_priv_param(u8_t* priv_param);
+
+#endif
+
+#endif /* LWIP_HDR_APPS_SNMP_V3_PRIV_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/sntp/sntp.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/sntp/sntp.c
new file mode 100644
index 0000000..71b2abe
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/sntp/sntp.c
@@ -0,0 +1,727 @@
+/**
+ * @file
+ * SNTP client module
+ */
+
+/*
+ * Copyright (c) 2007-2009 Frédéric Bernon, Simon Goldschmidt
+ * 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: Frédéric Bernon, Simon Goldschmidt
+ */
+
+
+/**
+ * @defgroup sntp SNTP
+ * @ingroup apps
+ *
+ * This is simple "SNTP" client for the lwIP raw API.
+ * It is a minimal implementation of SNTPv4 as specified in RFC 4330.
+ *
+ * For a list of some public NTP servers, see this link :
+ * http://support.ntp.org/bin/view/Servers/NTPPoolServers
+ *
+ * @todo:
+ * - set/change servers at runtime
+ * - complete SNTP_CHECK_RESPONSE checks 3 and 4
+ */
+
+#include "lwip/apps/sntp.h"
+
+#include "lwip/opt.h"
+#include "lwip/timeouts.h"
+#include "lwip/udp.h"
+#include "lwip/dns.h"
+#include "lwip/ip_addr.h"
+#include "lwip/pbuf.h"
+#include "lwip/dhcp.h"
+
+#include <string.h>
+#include <time.h>
+
+#if LWIP_UDP
+
+/* Handle support for more than one server via SNTP_MAX_SERVERS */
+#if SNTP_MAX_SERVERS > 1
+#define SNTP_SUPPORT_MULTIPLE_SERVERS 1
+#else /* NTP_MAX_SERVERS > 1 */
+#define SNTP_SUPPORT_MULTIPLE_SERVERS 0
+#endif /* NTP_MAX_SERVERS > 1 */
+
+#if (SNTP_UPDATE_DELAY < 15000) && !defined(SNTP_SUPPRESS_DELAY_CHECK)
+#error "SNTPv4 RFC 4330 enforces a minimum update time of 15 seconds (define SNTP_SUPPRESS_DELAY_CHECK to disable this error)!"
+#endif
+
+/* Configure behaviour depending on microsecond or second precision */
+#ifdef SNTP_SET_SYSTEM_TIME_US
+#define SNTP_CALC_TIME_US 1
+#define SNTP_RECEIVE_TIME_SIZE 2
+#else
+#define SNTP_SET_SYSTEM_TIME_US(sec, us)
+#define SNTP_CALC_TIME_US 0
+#define SNTP_RECEIVE_TIME_SIZE 1
+#endif
+
+
+/* the various debug levels for this file */
+#define SNTP_DEBUG_TRACE (SNTP_DEBUG | LWIP_DBG_TRACE)
+#define SNTP_DEBUG_STATE (SNTP_DEBUG | LWIP_DBG_STATE)
+#define SNTP_DEBUG_WARN (SNTP_DEBUG | LWIP_DBG_LEVEL_WARNING)
+#define SNTP_DEBUG_WARN_STATE (SNTP_DEBUG | LWIP_DBG_LEVEL_WARNING | LWIP_DBG_STATE)
+#define SNTP_DEBUG_SERIOUS (SNTP_DEBUG | LWIP_DBG_LEVEL_SERIOUS)
+
+#define SNTP_ERR_KOD 1
+
+/* SNTP protocol defines */
+#define SNTP_MSG_LEN 48
+
+#define SNTP_OFFSET_LI_VN_MODE 0
+#define SNTP_LI_MASK 0xC0
+#define SNTP_LI_NO_WARNING 0x00
+#define SNTP_LI_LAST_MINUTE_61_SEC 0x01
+#define SNTP_LI_LAST_MINUTE_59_SEC 0x02
+#define SNTP_LI_ALARM_CONDITION 0x03 /* (clock not synchronized) */
+
+#define SNTP_VERSION_MASK 0x38
+#define SNTP_VERSION (4/* NTP Version 4*/<<3)
+
+#define SNTP_MODE_MASK 0x07
+#define SNTP_MODE_CLIENT 0x03
+#define SNTP_MODE_SERVER 0x04
+#define SNTP_MODE_BROADCAST 0x05
+
+#define SNTP_OFFSET_STRATUM 1
+#define SNTP_STRATUM_KOD 0x00
+
+#define SNTP_OFFSET_ORIGINATE_TIME 24
+#define SNTP_OFFSET_RECEIVE_TIME 32
+#define SNTP_OFFSET_TRANSMIT_TIME 40
+
+/* number of seconds between 1900 and 1970 (MSB=1)*/
+#define DIFF_SEC_1900_1970 (2208988800UL)
+/* number of seconds between 1970 and Feb 7, 2036 (6:28:16 UTC) (MSB=0) */
+#define DIFF_SEC_1970_2036 (2085978496UL)
+
+/**
+ * SNTP packet format (without optional fields)
+ * Timestamps are coded as 64 bits:
+ * - 32 bits seconds since Jan 01, 1970, 00:00
+ * - 32 bits seconds fraction (0-padded)
+ * For future use, if the MSB in the seconds part is set, seconds are based
+ * on Feb 07, 2036, 06:28:16.
+ */
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct sntp_msg {
+ PACK_STRUCT_FLD_8(u8_t li_vn_mode);
+ PACK_STRUCT_FLD_8(u8_t stratum);
+ PACK_STRUCT_FLD_8(u8_t poll);
+ PACK_STRUCT_FLD_8(u8_t precision);
+ PACK_STRUCT_FIELD(u32_t root_delay);
+ PACK_STRUCT_FIELD(u32_t root_dispersion);
+ PACK_STRUCT_FIELD(u32_t reference_identifier);
+ PACK_STRUCT_FIELD(u32_t reference_timestamp[2]);
+ PACK_STRUCT_FIELD(u32_t originate_timestamp[2]);
+ PACK_STRUCT_FIELD(u32_t receive_timestamp[2]);
+ PACK_STRUCT_FIELD(u32_t transmit_timestamp[2]);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+/* function prototypes */
+static void sntp_request(void *arg);
+
+/** The operating mode */
+static u8_t sntp_opmode;
+
+/** The UDP pcb used by the SNTP client */
+static struct udp_pcb* sntp_pcb;
+/** Names/Addresses of servers */
+struct sntp_server {
+#if SNTP_SERVER_DNS
+ char* name;
+#endif /* SNTP_SERVER_DNS */
+ ip_addr_t addr;
+};
+static struct sntp_server sntp_servers[SNTP_MAX_SERVERS];
+
+#if SNTP_GET_SERVERS_FROM_DHCP
+static u8_t sntp_set_servers_from_dhcp;
+#endif /* SNTP_GET_SERVERS_FROM_DHCP */
+#if SNTP_SUPPORT_MULTIPLE_SERVERS
+/** The currently used server (initialized to 0) */
+static u8_t sntp_current_server;
+#else /* SNTP_SUPPORT_MULTIPLE_SERVERS */
+#define sntp_current_server 0
+#endif /* SNTP_SUPPORT_MULTIPLE_SERVERS */
+
+#if SNTP_RETRY_TIMEOUT_EXP
+#define SNTP_RESET_RETRY_TIMEOUT() sntp_retry_timeout = SNTP_RETRY_TIMEOUT
+/** Retry time, initialized with SNTP_RETRY_TIMEOUT and doubled with each retry. */
+static u32_t sntp_retry_timeout;
+#else /* SNTP_RETRY_TIMEOUT_EXP */
+#define SNTP_RESET_RETRY_TIMEOUT()
+#define sntp_retry_timeout SNTP_RETRY_TIMEOUT
+#endif /* SNTP_RETRY_TIMEOUT_EXP */
+
+#if SNTP_CHECK_RESPONSE >= 1
+/** Saves the last server address to compare with response */
+static ip_addr_t sntp_last_server_address;
+#endif /* SNTP_CHECK_RESPONSE >= 1 */
+
+#if SNTP_CHECK_RESPONSE >= 2
+/** Saves the last timestamp sent (which is sent back by the server)
+ * to compare against in response */
+static u32_t sntp_last_timestamp_sent[2];
+#endif /* SNTP_CHECK_RESPONSE >= 2 */
+
+/**
+ * SNTP processing of received timestamp
+ */
+static void
+sntp_process(u32_t *receive_timestamp)
+{
+ /* convert SNTP time (1900-based) to unix GMT time (1970-based)
+ * if MSB is 0, SNTP time is 2036-based!
+ */
+ u32_t rx_secs = lwip_ntohl(receive_timestamp[0]);
+ int is_1900_based = ((rx_secs & 0x80000000) != 0);
+ u32_t t = is_1900_based ? (rx_secs - DIFF_SEC_1900_1970) : (rx_secs + DIFF_SEC_1970_2036);
+ time_t tim = t;
+
+#if SNTP_CALC_TIME_US
+ u32_t us = lwip_ntohl(receive_timestamp[1]) / 4295;
+ SNTP_SET_SYSTEM_TIME_US(t, us);
+ /* display local time from GMT time */
+ LWIP_DEBUGF(SNTP_DEBUG_TRACE, ("sntp_process: %s, %"U32_F" us", ctime(&tim), us));
+
+#else /* SNTP_CALC_TIME_US */
+
+ /* change system time and/or the update the RTC clock */
+ SNTP_SET_SYSTEM_TIME(t);
+ /* display local time from GMT time */
+ LWIP_DEBUGF(SNTP_DEBUG_TRACE, ("sntp_process: %s", ctime(&tim)));
+#endif /* SNTP_CALC_TIME_US */
+ LWIP_UNUSED_ARG(tim);
+}
+
+/**
+ * Initialize request struct to be sent to server.
+ */
+static void
+sntp_initialize_request(struct sntp_msg *req)
+{
+ memset(req, 0, SNTP_MSG_LEN);
+ req->li_vn_mode = SNTP_LI_NO_WARNING | SNTP_VERSION | SNTP_MODE_CLIENT;
+
+#if SNTP_CHECK_RESPONSE >= 2
+ {
+ u32_t sntp_time_sec, sntp_time_us;
+ /* fill in transmit timestamp and save it in 'sntp_last_timestamp_sent' */
+ SNTP_GET_SYSTEM_TIME(sntp_time_sec, sntp_time_us);
+ sntp_last_timestamp_sent[0] = lwip_htonl(sntp_time_sec + DIFF_SEC_1900_1970);
+ req->transmit_timestamp[0] = sntp_last_timestamp_sent[0];
+ /* we send/save us instead of fraction to be faster... */
+ sntp_last_timestamp_sent[1] = lwip_htonl(sntp_time_us);
+ req->transmit_timestamp[1] = sntp_last_timestamp_sent[1];
+ }
+#endif /* SNTP_CHECK_RESPONSE >= 2 */
+}
+
+/**
+ * Retry: send a new request (and increase retry timeout).
+ *
+ * @param arg is unused (only necessary to conform to sys_timeout)
+ */
+static void
+sntp_retry(void* arg)
+{
+ LWIP_UNUSED_ARG(arg);
+
+ LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_retry: Next request will be sent in %"U32_F" ms\n",
+ sntp_retry_timeout));
+
+ /* set up a timer to send a retry and increase the retry delay */
+ sys_timeout(sntp_retry_timeout, sntp_request, NULL);
+
+#if SNTP_RETRY_TIMEOUT_EXP
+ {
+ u32_t new_retry_timeout;
+ /* increase the timeout for next retry */
+ new_retry_timeout = sntp_retry_timeout << 1;
+ /* limit to maximum timeout and prevent overflow */
+ if ((new_retry_timeout <= SNTP_RETRY_TIMEOUT_MAX) &&
+ (new_retry_timeout > sntp_retry_timeout)) {
+ sntp_retry_timeout = new_retry_timeout;
+ }
+ }
+#endif /* SNTP_RETRY_TIMEOUT_EXP */
+}
+
+#if SNTP_SUPPORT_MULTIPLE_SERVERS
+/**
+ * If Kiss-of-Death is received (or another packet parsing error),
+ * try the next server or retry the current server and increase the retry
+ * timeout if only one server is available.
+ * (implicitly, SNTP_MAX_SERVERS > 1)
+ *
+ * @param arg is unused (only necessary to conform to sys_timeout)
+ */
+static void
+sntp_try_next_server(void* arg)
+{
+ u8_t old_server, i;
+ LWIP_UNUSED_ARG(arg);
+
+ old_server = sntp_current_server;
+ for (i = 0; i < SNTP_MAX_SERVERS - 1; i++) {
+ sntp_current_server++;
+ if (sntp_current_server >= SNTP_MAX_SERVERS) {
+ sntp_current_server = 0;
+ }
+ if (!ip_addr_isany(&sntp_servers[sntp_current_server].addr)
+#if SNTP_SERVER_DNS
+ || (sntp_servers[sntp_current_server].name != NULL)
+#endif
+ ) {
+ LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_try_next_server: Sending request to server %"U16_F"\n",
+ (u16_t)sntp_current_server));
+ /* new server: reset retry timeout */
+ SNTP_RESET_RETRY_TIMEOUT();
+ /* instantly send a request to the next server */
+ sntp_request(NULL);
+ return;
+ }
+ }
+ /* no other valid server found */
+ sntp_current_server = old_server;
+ sntp_retry(NULL);
+}
+#else /* SNTP_SUPPORT_MULTIPLE_SERVERS */
+/* Always retry on error if only one server is supported */
+#define sntp_try_next_server sntp_retry
+#endif /* SNTP_SUPPORT_MULTIPLE_SERVERS */
+
+/** UDP recv callback for the sntp pcb */
+static void
+sntp_recv(void *arg, struct udp_pcb* pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
+{
+ u8_t mode;
+ u8_t stratum;
+ u32_t receive_timestamp[SNTP_RECEIVE_TIME_SIZE];
+ err_t err;
+
+ LWIP_UNUSED_ARG(arg);
+ LWIP_UNUSED_ARG(pcb);
+
+ /* packet received: stop retry timeout */
+ sys_untimeout(sntp_try_next_server, NULL);
+ sys_untimeout(sntp_request, NULL);
+
+ err = ERR_ARG;
+#if SNTP_CHECK_RESPONSE >= 1
+ /* check server address and port */
+ if (((sntp_opmode != SNTP_OPMODE_POLL) || ip_addr_cmp(addr, &sntp_last_server_address)) &&
+ (port == SNTP_PORT))
+#else /* SNTP_CHECK_RESPONSE >= 1 */
+ LWIP_UNUSED_ARG(addr);
+ LWIP_UNUSED_ARG(port);
+#endif /* SNTP_CHECK_RESPONSE >= 1 */
+ {
+ /* process the response */
+ if (p->tot_len == SNTP_MSG_LEN) {
+ pbuf_copy_partial(p, &mode, 1, SNTP_OFFSET_LI_VN_MODE);
+ mode &= SNTP_MODE_MASK;
+ /* if this is a SNTP response... */
+ if (((sntp_opmode == SNTP_OPMODE_POLL) && (mode == SNTP_MODE_SERVER)) ||
+ ((sntp_opmode == SNTP_OPMODE_LISTENONLY) && (mode == SNTP_MODE_BROADCAST))) {
+ pbuf_copy_partial(p, &stratum, 1, SNTP_OFFSET_STRATUM);
+ if (stratum == SNTP_STRATUM_KOD) {
+ /* Kiss-of-death packet. Use another server or increase UPDATE_DELAY. */
+ err = SNTP_ERR_KOD;
+ LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_recv: Received Kiss-of-Death\n"));
+ } else {
+#if SNTP_CHECK_RESPONSE >= 2
+ /* check originate_timetamp against sntp_last_timestamp_sent */
+ u32_t originate_timestamp[2];
+ pbuf_copy_partial(p, &originate_timestamp, 8, SNTP_OFFSET_ORIGINATE_TIME);
+ if ((originate_timestamp[0] != sntp_last_timestamp_sent[0]) ||
+ (originate_timestamp[1] != sntp_last_timestamp_sent[1]))
+ {
+ LWIP_DEBUGF(SNTP_DEBUG_WARN, ("sntp_recv: Invalid originate timestamp in response\n"));
+ } else
+#endif /* SNTP_CHECK_RESPONSE >= 2 */
+ /* @todo: add code for SNTP_CHECK_RESPONSE >= 3 and >= 4 here */
+ {
+ /* correct answer */
+ err = ERR_OK;
+ pbuf_copy_partial(p, &receive_timestamp, SNTP_RECEIVE_TIME_SIZE * 4, SNTP_OFFSET_TRANSMIT_TIME);
+ }
+ }
+ } else {
+ LWIP_DEBUGF(SNTP_DEBUG_WARN, ("sntp_recv: Invalid mode in response: %"U16_F"\n", (u16_t)mode));
+ /* wait for correct response */
+ err = ERR_TIMEOUT;
+ }
+ } else {
+ LWIP_DEBUGF(SNTP_DEBUG_WARN, ("sntp_recv: Invalid packet length: %"U16_F"\n", p->tot_len));
+ }
+ }
+#if SNTP_CHECK_RESPONSE >= 1
+ else {
+ /* packet from wrong remote address or port, wait for correct response */
+ err = ERR_TIMEOUT;
+ }
+#endif /* SNTP_CHECK_RESPONSE >= 1 */
+ pbuf_free(p);
+ if (err == ERR_OK) {
+ sntp_process(receive_timestamp);
+
+ /* Set up timeout for next request (only if poll response was received)*/
+ if (sntp_opmode == SNTP_OPMODE_POLL) {
+ /* Correct response, reset retry timeout */
+ SNTP_RESET_RETRY_TIMEOUT();
+
+ sys_timeout((u32_t)SNTP_UPDATE_DELAY, sntp_request, NULL);
+ LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_recv: Scheduled next time request: %"U32_F" ms\n",
+ (u32_t)SNTP_UPDATE_DELAY));
+ }
+ } else if (err != ERR_TIMEOUT) {
+ /* Errors are only processed in case of an explicit poll response */
+ if (sntp_opmode == SNTP_OPMODE_POLL) {
+ if (err == SNTP_ERR_KOD) {
+ /* Kiss-of-death packet. Use another server or increase UPDATE_DELAY. */
+ sntp_try_next_server(NULL);
+ } else {
+ /* another error, try the same server again */
+ sntp_retry(NULL);
+ }
+ }
+ }
+}
+
+/** Actually send an sntp request to a server.
+ *
+ * @param server_addr resolved IP address of the SNTP server
+ */
+static void
+sntp_send_request(const ip_addr_t *server_addr)
+{
+ struct pbuf* p;
+ p = pbuf_alloc(PBUF_TRANSPORT, SNTP_MSG_LEN, PBUF_RAM);
+ if (p != NULL) {
+ struct sntp_msg *sntpmsg = (struct sntp_msg *)p->payload;
+ LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_send_request: Sending request to server\n"));
+ /* initialize request message */
+ sntp_initialize_request(sntpmsg);
+ /* send request */
+ udp_sendto(sntp_pcb, p, server_addr, SNTP_PORT);
+ /* free the pbuf after sending it */
+ pbuf_free(p);
+ /* set up receive timeout: try next server or retry on timeout */
+ sys_timeout((u32_t)SNTP_RECV_TIMEOUT, sntp_try_next_server, NULL);
+#if SNTP_CHECK_RESPONSE >= 1
+ /* save server address to verify it in sntp_recv */
+ ip_addr_set(&sntp_last_server_address, server_addr);
+#endif /* SNTP_CHECK_RESPONSE >= 1 */
+ } else {
+ LWIP_DEBUGF(SNTP_DEBUG_SERIOUS, ("sntp_send_request: Out of memory, trying again in %"U32_F" ms\n",
+ (u32_t)SNTP_RETRY_TIMEOUT));
+ /* out of memory: set up a timer to send a retry */
+ sys_timeout((u32_t)SNTP_RETRY_TIMEOUT, sntp_request, NULL);
+ }
+}
+
+#if SNTP_SERVER_DNS
+/**
+ * DNS found callback when using DNS names as server address.
+ */
+static void
+sntp_dns_found(const char* hostname, const ip_addr_t *ipaddr, void *arg)
+{
+ LWIP_UNUSED_ARG(hostname);
+ LWIP_UNUSED_ARG(arg);
+
+ if (ipaddr != NULL) {
+ /* Address resolved, send request */
+ LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_dns_found: Server address resolved, sending request\n"));
+ sntp_send_request(ipaddr);
+ } else {
+ /* DNS resolving failed -> try another server */
+ LWIP_DEBUGF(SNTP_DEBUG_WARN_STATE, ("sntp_dns_found: Failed to resolve server address resolved, trying next server\n"));
+ sntp_try_next_server(NULL);
+ }
+}
+#endif /* SNTP_SERVER_DNS */
+
+/**
+ * Send out an sntp request.
+ *
+ * @param arg is unused (only necessary to conform to sys_timeout)
+ */
+static void
+sntp_request(void *arg)
+{
+ ip_addr_t sntp_server_address;
+ err_t err;
+
+ LWIP_UNUSED_ARG(arg);
+
+ /* initialize SNTP server address */
+#if SNTP_SERVER_DNS
+ if (sntp_servers[sntp_current_server].name) {
+ /* always resolve the name and rely on dns-internal caching & timeout */
+ ip_addr_set_zero(&sntp_servers[sntp_current_server].addr);
+ err = dns_gethostbyname(sntp_servers[sntp_current_server].name, &sntp_server_address,
+ sntp_dns_found, NULL);
+ if (err == ERR_INPROGRESS) {
+ /* DNS request sent, wait for sntp_dns_found being called */
+ LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_request: Waiting for server address to be resolved.\n"));
+ return;
+ } else if (err == ERR_OK) {
+ sntp_servers[sntp_current_server].addr = sntp_server_address;
+ }
+ } else
+#endif /* SNTP_SERVER_DNS */
+ {
+ sntp_server_address = sntp_servers[sntp_current_server].addr;
+ err = (ip_addr_isany_val(sntp_server_address)) ? ERR_ARG : ERR_OK;
+ }
+
+ if (err == ERR_OK) {
+ LWIP_DEBUGF(SNTP_DEBUG_TRACE, ("sntp_request: current server address is %s\n",
+ ipaddr_ntoa(&sntp_server_address)));
+ sntp_send_request(&sntp_server_address);
+ } else {
+ /* address conversion failed, try another server */
+ LWIP_DEBUGF(SNTP_DEBUG_WARN_STATE, ("sntp_request: Invalid server address, trying next server.\n"));
+ sys_timeout((u32_t)SNTP_RETRY_TIMEOUT, sntp_try_next_server, NULL);
+ }
+}
+
+/**
+ * @ingroup sntp
+ * Initialize this module.
+ * Send out request instantly or after SNTP_STARTUP_DELAY(_FUNC).
+ */
+void
+sntp_init(void)
+{
+#ifdef SNTP_SERVER_ADDRESS
+#if SNTP_SERVER_DNS
+ sntp_setservername(0, SNTP_SERVER_ADDRESS);
+#else
+#error SNTP_SERVER_ADDRESS string not supported SNTP_SERVER_DNS==0
+#endif
+#endif /* SNTP_SERVER_ADDRESS */
+
+ if (sntp_pcb == NULL) {
+ sntp_pcb = udp_new_ip_type(IPADDR_TYPE_ANY);
+ LWIP_ASSERT("Failed to allocate udp pcb for sntp client", sntp_pcb != NULL);
+ if (sntp_pcb != NULL) {
+ udp_recv(sntp_pcb, sntp_recv, NULL);
+
+ if (sntp_opmode == SNTP_OPMODE_POLL) {
+ SNTP_RESET_RETRY_TIMEOUT();
+#if SNTP_STARTUP_DELAY
+ sys_timeout((u32_t)SNTP_STARTUP_DELAY_FUNC, sntp_request, NULL);
+#else
+ sntp_request(NULL);
+#endif
+ } else if (sntp_opmode == SNTP_OPMODE_LISTENONLY) {
+ ip_set_option(sntp_pcb, SOF_BROADCAST);
+ udp_bind(sntp_pcb, IP_ANY_TYPE, SNTP_PORT);
+ }
+ }
+ }
+}
+
+/**
+ * @ingroup sntp
+ * Stop this module.
+ */
+void
+sntp_stop(void)
+{
+ if (sntp_pcb != NULL) {
+ sys_untimeout(sntp_request, NULL);
+ sys_untimeout(sntp_try_next_server, NULL);
+ udp_remove(sntp_pcb);
+ sntp_pcb = NULL;
+ }
+}
+
+/**
+ * @ingroup sntp
+ * Get enabled state.
+ */
+u8_t sntp_enabled(void)
+{
+ return (sntp_pcb != NULL)? 1 : 0;
+}
+
+/**
+ * @ingroup sntp
+ * Sets the operating mode.
+ * @param operating_mode one of the available operating modes
+ */
+void
+sntp_setoperatingmode(u8_t operating_mode)
+{
+ LWIP_ASSERT("Invalid operating mode", operating_mode <= SNTP_OPMODE_LISTENONLY);
+ LWIP_ASSERT("Operating mode must not be set while SNTP client is running", sntp_pcb == NULL);
+ sntp_opmode = operating_mode;
+}
+
+/**
+ * @ingroup sntp
+ * Gets the operating mode.
+ */
+u8_t
+sntp_getoperatingmode(void)
+{
+ return sntp_opmode;
+}
+
+#if SNTP_GET_SERVERS_FROM_DHCP
+/**
+ * Config SNTP server handling by IP address, name, or DHCP; clear table
+ * @param set_servers_from_dhcp enable or disable getting server addresses from dhcp
+ */
+void
+sntp_servermode_dhcp(int set_servers_from_dhcp)
+{
+ u8_t new_mode = set_servers_from_dhcp ? 1 : 0;
+ if (sntp_set_servers_from_dhcp != new_mode) {
+ sntp_set_servers_from_dhcp = new_mode;
+ }
+}
+#endif /* SNTP_GET_SERVERS_FROM_DHCP */
+
+/**
+ * @ingroup sntp
+ * Initialize one of the NTP servers by IP address
+ *
+ * @param idx the index of the NTP server to set must be < SNTP_MAX_SERVERS
+ * @param server IP address of the NTP server to set
+ */
+void
+sntp_setserver(u8_t idx, const ip_addr_t *server)
+{
+ if (idx < SNTP_MAX_SERVERS) {
+ if (server != NULL) {
+ sntp_servers[idx].addr = (*server);
+ } else {
+ ip_addr_set_zero(&sntp_servers[idx].addr);
+ }
+#if SNTP_SERVER_DNS
+ sntp_servers[idx].name = NULL;
+#endif
+ }
+}
+
+#if LWIP_DHCP && SNTP_GET_SERVERS_FROM_DHCP
+/**
+ * Initialize one of the NTP servers by IP address, required by DHCP
+ *
+ * @param numdns the index of the NTP server to set must be < SNTP_MAX_SERVERS
+ * @param dnsserver IP address of the NTP server to set
+ */
+void
+dhcp_set_ntp_servers(u8_t num, const ip4_addr_t *server)
+{
+ LWIP_DEBUGF(SNTP_DEBUG_TRACE, ("sntp: %s %u.%u.%u.%u as NTP server #%u via DHCP\n",
+ (sntp_set_servers_from_dhcp ? "Got" : "Rejected"),
+ ip4_addr1(server), ip4_addr2(server), ip4_addr3(server), ip4_addr4(server), num));
+ if (sntp_set_servers_from_dhcp && num) {
+ u8_t i;
+ for (i = 0; (i < num) && (i < SNTP_MAX_SERVERS); i++) {
+ ip_addr_t addr;
+ ip_addr_copy_from_ip4(addr, server[i]);
+ sntp_setserver(i, &addr);
+ }
+ for (i = num; i < SNTP_MAX_SERVERS; i++) {
+ sntp_setserver(i, NULL);
+ }
+ }
+}
+#endif /* LWIP_DHCP && SNTP_GET_SERVERS_FROM_DHCP */
+
+/**
+ * @ingroup sntp
+ * Obtain one of the currently configured by IP address (or DHCP) NTP servers
+ *
+ * @param idx the index of the NTP server
+ * @return IP address of the indexed NTP server or "ip_addr_any" if the NTP
+ * server has not been configured by address (or at all).
+ */
+const ip_addr_t*
+sntp_getserver(u8_t idx)
+{
+ if (idx < SNTP_MAX_SERVERS) {
+ return &sntp_servers[idx].addr;
+ }
+ return IP_ADDR_ANY;
+}
+
+#if SNTP_SERVER_DNS
+/**
+ * Initialize one of the NTP servers by name
+ *
+ * @param numdns the index of the NTP server to set must be < SNTP_MAX_SERVERS
+ * @param dnsserver DNS name of the NTP server to set, to be resolved at contact time
+ */
+void
+sntp_setservername(u8_t idx, char *server)
+{
+ if (idx < SNTP_MAX_SERVERS) {
+ sntp_servers[idx].name = server;
+ }
+}
+
+/**
+ * Obtain one of the currently configured by name NTP servers.
+ *
+ * @param numdns the index of the NTP server
+ * @return IP address of the indexed NTP server or NULL if the NTP
+ * server has not been configured by name (or at all)
+ */
+char *
+sntp_getservername(u8_t idx)
+{
+ if (idx < SNTP_MAX_SERVERS) {
+ return sntp_servers[idx].name;
+ }
+ return NULL;
+}
+#endif /* SNTP_SERVER_DNS */
+
+#endif /* LWIP_UDP */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/tftp/tftp_server.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/tftp/tftp_server.c
new file mode 100644
index 0000000..243b092
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/tftp/tftp_server.c
@@ -0,0 +1,417 @@
+/****************************************************************//**
+ *
+ * @file tftp_server.c
+ *
+ * @author Logan Gunthorpe <logang@deltatee.com>
+ * Dirk Ziegelmeier <dziegel@gmx.de>
+ *
+ * @brief Trivial File Transfer Protocol (RFC 1350)
+ *
+ * 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>
+ * Dirk Ziegelmeier <dziegel@gmx.de>
+ *
+ */
+
+/**
+ * @defgroup tftp TFTP server
+ * @ingroup apps
+ *
+ * This is simple TFTP server for the lwIP raw API.
+ */
+
+#include "lwip/apps/tftp_server.h"
+
+#if LWIP_UDP
+
+#include "lwip/udp.h"
+#include "lwip/timeouts.h"
+#include "lwip/debug.h"
+
+#define TFTP_MAX_PAYLOAD_SIZE 512
+#define TFTP_HEADER_LENGTH 4
+
+#define TFTP_RRQ 1
+#define TFTP_WRQ 2
+#define TFTP_DATA 3
+#define TFTP_ACK 4
+#define TFTP_ERROR 5
+
+enum tftp_error {
+ TFTP_ERROR_FILE_NOT_FOUND = 1,
+ TFTP_ERROR_ACCESS_VIOLATION = 2,
+ TFTP_ERROR_DISK_FULL = 3,
+ TFTP_ERROR_ILLEGAL_OPERATION = 4,
+ TFTP_ERROR_UNKNOWN_TRFR_ID = 5,
+ TFTP_ERROR_FILE_EXISTS = 6,
+ TFTP_ERROR_NO_SUCH_USER = 7
+};
+
+#include <string.h>
+
+struct tftp_state {
+ const struct tftp_context *ctx;
+ void *handle;
+ struct pbuf *last_data;
+ struct udp_pcb *upcb;
+ ip_addr_t addr;
+ u16_t port;
+ int timer;
+ int last_pkt;
+ u16_t blknum;
+ u8_t retries;
+ u8_t mode_write;
+};
+
+static struct tftp_state tftp_state;
+
+static void tftp_tmr(void* arg);
+
+static void
+close_handle(void)
+{
+ tftp_state.port = 0;
+ ip_addr_set_any(0, &tftp_state.addr);
+
+ if(tftp_state.last_data != NULL) {
+ pbuf_free(tftp_state.last_data);
+ tftp_state.last_data = NULL;
+ }
+
+ sys_untimeout(tftp_tmr, NULL);
+
+ if (tftp_state.handle) {
+ tftp_state.ctx->close(tftp_state.handle);
+ tftp_state.handle = NULL;
+ LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, ("tftp: closing\n"));
+ }
+}
+
+static void
+send_error(const ip_addr_t *addr, u16_t port, enum tftp_error code, const char *str)
+{
+ int str_length = strlen(str);
+ struct pbuf* p;
+ u16_t* payload;
+
+ p = pbuf_alloc(PBUF_TRANSPORT, (u16_t)(TFTP_HEADER_LENGTH + str_length + 1), PBUF_RAM);
+ if(p == NULL) {
+ return;
+ }
+
+ payload = (u16_t*) p->payload;
+ payload[0] = PP_HTONS(TFTP_ERROR);
+ payload[1] = lwip_htons(code);
+ MEMCPY(&payload[2], str, str_length + 1);
+
+ udp_sendto(tftp_state.upcb, p, addr, port);
+ pbuf_free(p);
+}
+
+static void
+send_ack(u16_t blknum)
+{
+ struct pbuf* p;
+ u16_t* payload;
+
+ p = pbuf_alloc(PBUF_TRANSPORT, TFTP_HEADER_LENGTH, PBUF_RAM);
+ if(p == NULL) {
+ return;
+ }
+ payload = (u16_t*) p->payload;
+
+ payload[0] = PP_HTONS(TFTP_ACK);
+ payload[1] = lwip_htons(blknum);
+ udp_sendto(tftp_state.upcb, p, &tftp_state.addr, tftp_state.port);
+ pbuf_free(p);
+}
+
+static void
+resend_data(void)
+{
+ struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, tftp_state.last_data->len, PBUF_RAM);
+ if(p == NULL) {
+ return;
+ }
+
+ if(pbuf_copy(p, tftp_state.last_data) != ERR_OK) {
+ pbuf_free(p);
+ return;
+ }
+
+ udp_sendto(tftp_state.upcb, p, &tftp_state.addr, tftp_state.port);
+ pbuf_free(p);
+}
+
+static void
+send_data(void)
+{
+ u16_t *payload;
+ int ret;
+
+ if(tftp_state.last_data != NULL) {
+ pbuf_free(tftp_state.last_data);
+ }
+
+ tftp_state.last_data = pbuf_alloc(PBUF_TRANSPORT, TFTP_HEADER_LENGTH + TFTP_MAX_PAYLOAD_SIZE, PBUF_RAM);
+ if(tftp_state.last_data == NULL) {
+ return;
+ }
+
+ payload = (u16_t *) tftp_state.last_data->payload;
+ payload[0] = PP_HTONS(TFTP_DATA);
+ payload[1] = lwip_htons(tftp_state.blknum);
+
+ ret = tftp_state.ctx->read(tftp_state.handle, &payload[2], TFTP_MAX_PAYLOAD_SIZE);
+ if (ret < 0) {
+ send_error(&tftp_state.addr, tftp_state.port, TFTP_ERROR_ACCESS_VIOLATION, "Error occured while reading the file.");
+ close_handle();
+ return;
+ }
+
+ pbuf_realloc(tftp_state.last_data, (u16_t)(TFTP_HEADER_LENGTH + ret));
+ resend_data();
+}
+
+static void
+recv(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
+{
+ u16_t *sbuf = (u16_t *) p->payload;
+ int opcode;
+
+ LWIP_UNUSED_ARG(arg);
+ LWIP_UNUSED_ARG(upcb);
+
+ if (((tftp_state.port != 0) && (port != tftp_state.port)) ||
+ (!ip_addr_isany_val(tftp_state.addr) && !ip_addr_cmp(&tftp_state.addr, addr))) {
+ send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Only one connection at a time is supported");
+ pbuf_free(p);
+ return;
+ }
+
+ opcode = sbuf[0];
+
+ tftp_state.last_pkt = tftp_state.timer;
+ tftp_state.retries = 0;
+
+ switch (opcode) {
+ case PP_HTONS(TFTP_RRQ): /* fall through */
+ case PP_HTONS(TFTP_WRQ):
+ {
+ const char tftp_null = 0;
+ char filename[TFTP_MAX_FILENAME_LEN];
+ char mode[TFTP_MAX_MODE_LEN];
+ u16_t filename_end_offset;
+ u16_t mode_end_offset;
+
+ if(tftp_state.handle != NULL) {
+ send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Only one connection at a time is supported");
+ break;
+ }
+
+ sys_timeout(TFTP_TIMER_MSECS, tftp_tmr, NULL);
+
+ /* find \0 in pbuf -> end of filename string */
+ filename_end_offset = pbuf_memfind(p, &tftp_null, sizeof(tftp_null), 2);
+ if((u16_t)(filename_end_offset-2) > sizeof(filename)) {
+ send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Filename too long/not NULL terminated");
+ break;
+ }
+ pbuf_copy_partial(p, filename, filename_end_offset-2, 2);
+
+ /* find \0 in pbuf -> end of mode string */
+ mode_end_offset = pbuf_memfind(p, &tftp_null, sizeof(tftp_null), filename_end_offset+1);
+ if((u16_t)(mode_end_offset-filename_end_offset) > sizeof(mode)) {
+ send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Mode too long/not NULL terminated");
+ break;
+ }
+ pbuf_copy_partial(p, mode, mode_end_offset-filename_end_offset, filename_end_offset+1);
+
+ tftp_state.handle = tftp_state.ctx->open(filename, mode, opcode == PP_HTONS(TFTP_WRQ));
+ tftp_state.blknum = 1;
+
+ if (!tftp_state.handle) {
+ send_error(addr, port, TFTP_ERROR_FILE_NOT_FOUND, "Unable to open requested file.");
+ break;
+ }
+
+ LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, ("tftp: %s request from ", (opcode == PP_HTONS(TFTP_WRQ)) ? "write" : "read"));
+ ip_addr_debug_print(TFTP_DEBUG | LWIP_DBG_STATE, addr);
+ LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, (" for '%s' mode '%s'\n", filename, mode));
+
+ ip_addr_copy(tftp_state.addr, *addr);
+ tftp_state.port = port;
+
+ if (opcode == PP_HTONS(TFTP_WRQ)) {
+ tftp_state.mode_write = 1;
+ send_ack(0);
+ } else {
+ tftp_state.mode_write = 0;
+ send_data();
+ }
+
+ break;
+ }
+
+ case PP_HTONS(TFTP_DATA):
+ {
+ int ret;
+ u16_t blknum;
+
+ if (tftp_state.handle == NULL) {
+ send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "No connection");
+ break;
+ }
+
+ if (tftp_state.mode_write != 1) {
+ send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Not a write connection");
+ break;
+ }
+
+ blknum = lwip_ntohs(sbuf[1]);
+ pbuf_header(p, -TFTP_HEADER_LENGTH);
+
+ ret = tftp_state.ctx->write(tftp_state.handle, p);
+ if (ret < 0) {
+ send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "error writing file");
+ close_handle();
+ } else {
+ send_ack(blknum);
+ }
+
+ if (p->tot_len < TFTP_MAX_PAYLOAD_SIZE) {
+ close_handle();
+ }
+ break;
+ }
+
+ case PP_HTONS(TFTP_ACK):
+ {
+ u16_t blknum;
+ int lastpkt;
+
+ if (tftp_state.handle == NULL) {
+ send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "No connection");
+ break;
+ }
+
+ if (tftp_state.mode_write != 0) {
+ send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Not a read connection");
+ break;
+ }
+
+ blknum = lwip_ntohs(sbuf[1]);
+ if (blknum != tftp_state.blknum) {
+ send_error(addr, port, TFTP_ERROR_UNKNOWN_TRFR_ID, "Wrong block number");
+ break;
+ }
+
+ lastpkt = 0;
+
+ if (tftp_state.last_data != NULL) {
+ lastpkt = tftp_state.last_data->tot_len != (TFTP_MAX_PAYLOAD_SIZE + TFTP_HEADER_LENGTH);
+ }
+
+ if (!lastpkt) {
+ tftp_state.blknum++;
+ send_data();
+ } else {
+ close_handle();
+ }
+
+ break;
+ }
+
+ default:
+ send_error(addr, port, TFTP_ERROR_ILLEGAL_OPERATION, "Unknown operation");
+ break;
+ }
+
+ pbuf_free(p);
+}
+
+static void
+tftp_tmr(void* arg)
+{
+ LWIP_UNUSED_ARG(arg);
+
+ tftp_state.timer++;
+
+ if (tftp_state.handle == NULL) {
+ return;
+ }
+
+ sys_timeout(TFTP_TIMER_MSECS, tftp_tmr, NULL);
+
+ if ((tftp_state.timer - tftp_state.last_pkt) > (TFTP_TIMEOUT_MSECS / TFTP_TIMER_MSECS)) {
+ if ((tftp_state.last_data != NULL) && (tftp_state.retries < TFTP_MAX_RETRIES)) {
+ LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, ("tftp: timeout, retrying\n"));
+ resend_data();
+ tftp_state.retries++;
+ } else {
+ LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, ("tftp: timeout\n"));
+ close_handle();
+ }
+ }
+}
+
+/** @ingroup tftp
+ * Initialize TFTP server.
+ * @param ctx TFTP callback struct
+ */
+err_t
+tftp_init(const struct tftp_context *ctx)
+{
+ err_t ret;
+
+ struct udp_pcb *pcb = udp_new_ip_type(IPADDR_TYPE_ANY);
+ if (pcb == NULL) {
+ return ERR_MEM;
+ }
+
+ ret = udp_bind(pcb, IP_ANY_TYPE, TFTP_PORT);
+ if (ret != ERR_OK) {
+ udp_remove(pcb);
+ return ret;
+ }
+
+ tftp_state.handle = NULL;
+ tftp_state.port = 0;
+ tftp_state.ctx = ctx;
+ tftp_state.timer = 0;
+ tftp_state.last_data = NULL;
+ tftp_state.upcb = pcb;
+
+ udp_recv(pcb, recv, NULL);
+
+ return ERR_OK;
+}
+
+#endif /* LWIP_UDP */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/def.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/def.c
new file mode 100644
index 0000000..8125313
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/def.c
@@ -0,0 +1,222 @@
+/**
+ * @file
+ * Common functions used throughout the stack.
+ *
+ * These are reference implementations of the byte swapping functions.
+ * Again with the aim of being simple, correct and fully portable.
+ * Byte swapping is the second thing you would want to optimize. You will
+ * need to port it to your architecture and in your cc.h:
+ *
+ * \#define lwip_htons(x) your_htons
+ * \#define lwip_htonl(x) your_htonl
+ *
+ * Note lwip_ntohs() and lwip_ntohl() are merely references to the htonx counterparts.
+ *
+ * If you \#define them to htons() and htonl(), you should
+ * \#define LWIP_DONT_PROVIDE_BYTEORDER_FUNCTIONS to prevent lwIP from
+ * defining htonx/ntohx compatibility macros.
+
+ * @defgroup sys_nonstandard Non-standard functions
+ * @ingroup sys_layer
+ * lwIP provides default implementations for non-standard functions.
+ * These can be mapped to OS functions to reduce code footprint if desired.
+ * All defines related to this section must not be placed in lwipopts.h,
+ * but in arch/cc.h!
+ * These options cannot be \#defined in lwipopts.h since they are not options
+ * of lwIP itself, but options of the lwIP port to your system.
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Simon Goldschmidt
+ *
+ */
+
+#include "lwip/opt.h"
+#include "lwip/def.h"
+
+#include <string.h>
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+
+#if !defined(lwip_htons)
+/**
+ * Convert an u16_t from host- to network byte order.
+ *
+ * @param n u16_t in host byte order
+ * @return n in network byte order
+ */
+u16_t
+lwip_htons(u16_t n)
+{
+ return (u16_t)PP_HTONS(n);
+}
+#endif /* lwip_htons */
+
+#if !defined(lwip_htonl)
+/**
+ * Convert an u32_t from host- to network byte order.
+ *
+ * @param n u32_t in host byte order
+ * @return n in network byte order
+ */
+u32_t
+lwip_htonl(u32_t n)
+{
+ return (u32_t)PP_HTONL(n);
+}
+#endif /* lwip_htonl */
+
+#endif /* BYTE_ORDER == LITTLE_ENDIAN */
+
+#ifndef lwip_strnstr
+/**
+ * @ingroup sys_nonstandard
+ * lwIP default implementation for strnstr() non-standard function.
+ * This can be \#defined to strnstr() depending on your platform port.
+ */
+char*
+lwip_strnstr(const char* buffer, const char* token, size_t n)
+{
+ const char* p;
+ size_t tokenlen = strlen(token);
+ if (tokenlen == 0) {
+ return LWIP_CONST_CAST(char *, buffer);
+ }
+ for (p = buffer; *p && (p + tokenlen <= buffer + n); p++) {
+ if ((*p == *token) && (strncmp(p, token, tokenlen) == 0)) {
+ return LWIP_CONST_CAST(char *, p);
+ }
+ }
+ return NULL;
+}
+#endif
+
+#ifndef lwip_stricmp
+/**
+ * @ingroup sys_nonstandard
+ * lwIP default implementation for stricmp() non-standard function.
+ * This can be \#defined to stricmp() depending on your platform port.
+ */
+int
+lwip_stricmp(const char* str1, const char* str2)
+{
+ char c1, c2;
+
+ do {
+ c1 = *str1++;
+ c2 = *str2++;
+ if (c1 != c2) {
+ char c1_upc = c1 | 0x20;
+ if ((c1_upc >= 'a') && (c1_upc <= 'z')) {
+ /* characters are not equal an one is in the alphabet range:
+ downcase both chars and check again */
+ char c2_upc = c2 | 0x20;
+ if (c1_upc != c2_upc) {
+ /* still not equal */
+ /* don't care for < or > */
+ return 1;
+ }
+ } else {
+ /* characters are not equal but none is in the alphabet range */
+ return 1;
+ }
+ }
+ } while (c1 != 0);
+ return 0;
+}
+#endif
+
+#ifndef lwip_strnicmp
+/**
+ * @ingroup sys_nonstandard
+ * lwIP default implementation for strnicmp() non-standard function.
+ * This can be \#defined to strnicmp() depending on your platform port.
+ */
+int
+lwip_strnicmp(const char* str1, const char* str2, size_t len)
+{
+ char c1, c2;
+
+ do {
+ c1 = *str1++;
+ c2 = *str2++;
+ if (c1 != c2) {
+ char c1_upc = c1 | 0x20;
+ if ((c1_upc >= 'a') && (c1_upc <= 'z')) {
+ /* characters are not equal an one is in the alphabet range:
+ downcase both chars and check again */
+ char c2_upc = c2 | 0x20;
+ if (c1_upc != c2_upc) {
+ /* still not equal */
+ /* don't care for < or > */
+ return 1;
+ }
+ } else {
+ /* characters are not equal but none is in the alphabet range */
+ return 1;
+ }
+ }
+ } while (len-- && c1 != 0);
+ return 0;
+}
+#endif
+
+#ifndef lwip_itoa
+/**
+ * @ingroup sys_nonstandard
+ * lwIP default implementation for itoa() non-standard function.
+ * This can be \#defined to itoa() or snprintf(result, bufsize, "%d", number) depending on your platform port.
+ */
+void
+lwip_itoa(char* result, size_t bufsize, int number)
+{
+ const int base = 10;
+ char* ptr = result, *ptr1 = result, tmp_char;
+ int tmp_value;
+ LWIP_UNUSED_ARG(bufsize);
+
+ do {
+ tmp_value = number;
+ number /= base;
+ *ptr++ = "zyxwvutsrqponmlkjihgfedcba9876543210123456789abcdefghijklmnopqrstuvwxyz"[35 + (tmp_value - number * base)];
+ } while(number);
+
+ /* Apply negative sign */
+ if (tmp_value < 0) {
+ *ptr++ = '-';
+ }
+ *ptr-- = '\0';
+ while(ptr1 < ptr) {
+ tmp_char = *ptr;
+ *ptr--= *ptr1;
+ *ptr1++ = tmp_char;
+ }
+}
+#endif
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/dhcp.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/dhcp.c
new file mode 100644
index 0000000..92f268d
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/dhcp.c
@@ -0,0 +1,1812 @@
+/**
+ * @file
+ * Dynamic Host Configuration Protocol client
+ *
+ */
+
+/*
+ *
+ * Copyright (c) 2001-2004 Leon Woestenberg <leon.woestenberg@gmx.net>
+ * Copyright (c) 2001-2004 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is a contribution to the lwIP TCP/IP stack.
+ * The Swedish Institute of Computer Science and Adam Dunkels
+ * are specifically granted permission to redistribute this
+ * source code.
+ *
+ * Author: Leon Woestenberg <leon.woestenberg@gmx.net>
+ *
+ * This is a DHCP client for the lwIP TCP/IP stack. It aims to conform
+ * with RFC 2131 and RFC 2132.
+ *
+ * TODO:
+ * - Support for interfaces other than Ethernet (SLIP, PPP, ...)
+ *
+ * Please coordinate changes and requests with Leon Woestenberg
+ * <leon.woestenberg@gmx.net>
+ *
+ * Integration with your code:
+ *
+ * In lwip/dhcp.h
+ * #define DHCP_COARSE_TIMER_SECS (recommended 60 which is a minute)
+ * #define DHCP_FINE_TIMER_MSECS (recommended 500 which equals TCP coarse timer)
+ *
+ * Then have your application call dhcp_coarse_tmr() and
+ * dhcp_fine_tmr() on the defined intervals.
+ *
+ * dhcp_start(struct netif *netif);
+ * starts a DHCP client instance which configures the interface by
+ * obtaining an IP address lease and maintaining it.
+ *
+ * Use dhcp_release(netif) to end the lease and use dhcp_stop(netif)
+ * to remove the DHCP client.
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_DHCP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/stats.h"
+#include "lwip/mem.h"
+#include "lwip/udp.h"
+#include "lwip/ip_addr.h"
+#include "lwip/netif.h"
+#include "lwip/def.h"
+#include "lwip/dhcp.h"
+#include "lwip/autoip.h"
+#include "lwip/dns.h"
+#include "netif/etharp.h"
+
+#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"
+ * #define DHCP_GLOBAL_XID rand()
+ */
+#ifdef DHCP_GLOBAL_XID_HEADER
+#include DHCP_GLOBAL_XID_HEADER /* include optional starting XID generation prototypes */
+#endif
+
+/** DHCP_OPTION_MAX_MSG_SIZE is set to the MTU
+ * MTU is checked to be big enough in dhcp_start */
+#define DHCP_MAX_MSG_LEN(netif) (netif->mtu)
+#define DHCP_MAX_MSG_LEN_MIN_REQUIRED 576
+/** Minimum length for reply before packet is parsed */
+#define DHCP_MIN_REPLY_LEN 44
+
+#define REBOOT_TRIES 2
+
+#if LWIP_DHCP_GET_NTP_SRV
+/** This function must exist, in other to add offered NTP servers to
+ * the NTP (or SNTP) engine.
+ * See LWIP_DHCP_MAX_NTP_SERVERS */
+extern void dhcp_set_ntp_servers(u8_t num_ntp_servers, ip_addr_t* ntp_server_addrs);
+#endif /* LWIP_DHCP_GET_NTP_SRV */
+
+/** Option handling: options are parsed in dhcp_parse_reply
+ * and saved in an array where other functions can load them from.
+ * This might be moved into the struct dhcp (not necessarily since
+ * lwIP is single-threaded and the array is only used while in recv
+ * callback). */
+#define DHCP_OPTION_IDX_OVERLOAD 0
+#define DHCP_OPTION_IDX_MSG_TYPE 1
+#define DHCP_OPTION_IDX_SERVER_ID 2
+#define DHCP_OPTION_IDX_LEASE_TIME 3
+#define DHCP_OPTION_IDX_T1 4
+#define DHCP_OPTION_IDX_T2 5
+#define DHCP_OPTION_IDX_SUBNET_MASK 6
+#define DHCP_OPTION_IDX_ROUTER 7
+#define DHCP_OPTION_IDX_DNS_SERVER 8
+#if LWIP_DHCP_GET_NTP_SRV
+#define DHCP_OPTION_IDX_NTP_SERVER (DHCP_OPTION_IDX_DNS_SERVER + DNS_MAX_SERVERS)
+#define DHCP_OPTION_IDX_MAX (DHCP_OPTION_IDX_NTP_SERVER + LWIP_DHCP_MAX_NTP_SERVERS)
+#else /* LWIP_DHCP_GET_NTP_SRV */
+#define DHCP_OPTION_IDX_MAX (DHCP_OPTION_IDX_DNS_SERVER + DNS_MAX_SERVERS)
+#endif /* LWIP_DHCP_GET_NTP_SRV */
+
+/** Holds the decoded option values, only valid while in dhcp_recv.
+ @todo: move this into struct dhcp? */
+u32_t dhcp_rx_options_val[DHCP_OPTION_IDX_MAX];
+/** Holds a flag which option was received and is contained in dhcp_rx_options_val,
+ only valid while in dhcp_recv.
+ @todo: move this into struct dhcp? */
+u8_t dhcp_rx_options_given[DHCP_OPTION_IDX_MAX];
+
+static u8_t dhcp_discover_select_options[] = {
+ DHCP_OPTION_SUBNET_MASK,
+ DHCP_OPTION_ROUTER,
+ DHCP_OPTION_BROADCAST,
+ DHCP_OPTION_DNS_SERVER
+#if LWIP_DHCP_GET_NTP_SRV
+ , DHCP_OPTION_NTP
+#endif /* LWIP_DHCP_GET_NTP_SRV */
+ };
+
+#ifdef DHCP_GLOBAL_XID
+static u32_t xid;
+static u8_t xid_initialised;
+#endif /* DHCP_GLOBAL_XID */
+
+#define dhcp_option_given(dhcp, idx) (dhcp_rx_options_given[idx] != 0)
+#define dhcp_got_option(dhcp, idx) (dhcp_rx_options_given[idx] = 1)
+#define dhcp_clear_option(dhcp, idx) (dhcp_rx_options_given[idx] = 0)
+#define dhcp_clear_all_options(dhcp) (memset(dhcp_rx_options_given, 0, sizeof(dhcp_rx_options_given)))
+#define dhcp_get_option_value(dhcp, idx) (dhcp_rx_options_val[idx])
+#define dhcp_set_option_value(dhcp, idx, val) (dhcp_rx_options_val[idx] = (val))
+
+
+/* DHCP client state machine functions */
+static err_t dhcp_discover(struct netif *netif);
+static err_t dhcp_select(struct netif *netif);
+static void dhcp_bind(struct netif *netif);
+#if DHCP_DOES_ARP_CHECK
+static err_t dhcp_decline(struct netif *netif);
+#endif /* DHCP_DOES_ARP_CHECK */
+static err_t dhcp_rebind(struct netif *netif);
+static err_t dhcp_reboot(struct netif *netif);
+static void dhcp_set_state(struct dhcp *dhcp, u8_t new_state);
+
+/* receive, unfold, parse and free incoming messages */
+static void dhcp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port);
+
+/* set the DHCP timers */
+static void dhcp_timeout(struct netif *netif);
+static void dhcp_t1_timeout(struct netif *netif);
+static void dhcp_t2_timeout(struct netif *netif);
+
+/* build outgoing messages */
+/* create a DHCP message, fill in common headers */
+static err_t dhcp_create_msg(struct netif *netif, struct dhcp *dhcp, u8_t message_type);
+/* free a DHCP request */
+static void dhcp_delete_msg(struct dhcp *dhcp);
+/* add a DHCP option (type, then length in bytes) */
+static void dhcp_option(struct dhcp *dhcp, u8_t option_type, u8_t option_len);
+/* add option values */
+static void dhcp_option_byte(struct dhcp *dhcp, u8_t value);
+static void dhcp_option_short(struct dhcp *dhcp, u16_t value);
+static void dhcp_option_long(struct dhcp *dhcp, u32_t value);
+#if LWIP_NETIF_HOSTNAME
+static void dhcp_option_hostname(struct dhcp *dhcp, struct netif *netif);
+#endif /* LWIP_NETIF_HOSTNAME */
+/* always add the DHCP options trailer to end and pad */
+static void dhcp_option_trailer(struct dhcp *dhcp);
+
+/**
+ * Back-off the DHCP client (because of a received NAK response).
+ *
+ * Back-off the DHCP client because of a received NAK. Receiving a
+ * NAK means the client asked for something non-sensible, for
+ * example when it tries to renew a lease obtained on another network.
+ *
+ * We clear any existing set IP address and restart DHCP negotiation
+ * afresh (as per RFC2131 3.2.3).
+ *
+ * @param netif the netif under DHCP control
+ */
+static void
+dhcp_handle_nak(struct netif *netif)
+{
+ struct dhcp *dhcp = netif->dhcp;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_handle_nak(netif=%p) %c%c%"U16_F"\n",
+ (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num));
+ /* remove IP address from interface (must no longer be used, as per RFC2131) */
+ netif_set_addr(netif, IP_ADDR_ANY, IP_ADDR_ANY, IP_ADDR_ANY);
+ /* Change to a defined state */
+ dhcp_set_state(dhcp, DHCP_BACKING_OFF);
+ /* We can immediately restart discovery */
+ dhcp_discover(netif);
+}
+
+#if DHCP_DOES_ARP_CHECK
+/**
+ * Checks if the offered IP address is already in use.
+ *
+ * It does so by sending an ARP request for the offered address and
+ * entering CHECKING state. If no ARP reply is received within a small
+ * interval, the address is assumed to be free for use by us.
+ *
+ * @param netif the netif under DHCP control
+ */
+static void
+dhcp_check(struct netif *netif)
+{
+ struct dhcp *dhcp = netif->dhcp;
+ err_t result;
+ u16_t msecs;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_check(netif=%p) %c%c\n", (void *)netif, (s16_t)netif->name[0],
+ (s16_t)netif->name[1]));
+ dhcp_set_state(dhcp, DHCP_CHECKING);
+ /* create an ARP query for the offered IP address, expecting that no host
+ responds, as the IP address should not be in use. */
+ result = etharp_query(netif, &dhcp->offered_ip_addr, NULL);
+ if (result != ERR_OK) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("dhcp_check: could not perform ARP query\n"));
+ }
+ dhcp->tries++;
+ msecs = 500;
+ dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_check(): set request timeout %"U16_F" msecs\n", msecs));
+}
+#endif /* DHCP_DOES_ARP_CHECK */
+
+/**
+ * Remember the configuration offered by a DHCP server.
+ *
+ * @param netif the netif under DHCP control
+ */
+static void
+dhcp_handle_offer(struct netif *netif)
+{
+ struct dhcp *dhcp = netif->dhcp;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_handle_offer(netif=%p) %c%c%"U16_F"\n",
+ (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num));
+ /* obtain the server address */
+ if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_SERVER_ID)) {
+ ip4_addr_set_u32(&dhcp->server_ip_addr, htonl(dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_SERVER_ID)));
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_handle_offer(): server 0x%08"X32_F"\n",
+ ip4_addr_get_u32(&dhcp->server_ip_addr)));
+ /* remember offered address */
+ ip_addr_copy(dhcp->offered_ip_addr, dhcp->msg_in->yiaddr);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_handle_offer(): offer for 0x%08"X32_F"\n",
+ ip4_addr_get_u32(&dhcp->offered_ip_addr)));
+
+ dhcp_select(netif);
+ } else {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
+ ("dhcp_handle_offer(netif=%p) did not get server ID!\n", (void*)netif));
+ }
+}
+
+/**
+ * Select a DHCP server offer out of all offers.
+ *
+ * Simply select the first offer received.
+ *
+ * @param netif the netif under DHCP control
+ * @return lwIP specific error (see error.h)
+ */
+static err_t
+dhcp_select(struct netif *netif)
+{
+ struct dhcp *dhcp = netif->dhcp;
+ err_t result;
+ u16_t msecs;
+ u8_t i;
+
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_select(netif=%p) %c%c%"U16_F"\n", (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num));
+ dhcp_set_state(dhcp, DHCP_REQUESTING);
+
+ /* create and initialize the DHCP message header */
+ result = dhcp_create_msg(netif, dhcp, DHCP_REQUEST);
+ if (result == ERR_OK) {
+ dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
+ dhcp_option_short(dhcp, DHCP_MAX_MSG_LEN(netif));
+
+ /* MUST request the offered IP address */
+ dhcp_option(dhcp, DHCP_OPTION_REQUESTED_IP, 4);
+ dhcp_option_long(dhcp, ntohl(ip4_addr_get_u32(&dhcp->offered_ip_addr)));
+
+ dhcp_option(dhcp, DHCP_OPTION_SERVER_ID, 4);
+ dhcp_option_long(dhcp, ntohl(ip4_addr_get_u32(&dhcp->server_ip_addr)));
+
+ dhcp_option(dhcp, DHCP_OPTION_PARAMETER_REQUEST_LIST, sizeof(dhcp_discover_select_options));
+ for (i = 0; i < sizeof(dhcp_discover_select_options); i++) {
+ dhcp_option_byte(dhcp, dhcp_discover_select_options[i]);
+ }
+
+#if LWIP_NETIF_HOSTNAME
+ dhcp_option_hostname(dhcp, netif);
+#endif /* LWIP_NETIF_HOSTNAME */
+
+ dhcp_option_trailer(dhcp);
+ /* shrink the pbuf to the actual content length */
+ pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len);
+
+ /* send broadcast to any DHCP server */
+ udp_sendto_if_src(dhcp->pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif, IP_ADDR_ANY);
+ dhcp_delete_msg(dhcp);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_select: REQUESTING\n"));
+ } else {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("dhcp_select: could not allocate DHCP request\n"));
+ }
+ dhcp->tries++;
+ msecs = (dhcp->tries < 6 ? 1 << dhcp->tries : 60) * 1000;
+ dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_select(): set request timeout %"U16_F" msecs\n", msecs));
+ return result;
+}
+
+/**
+ * The DHCP timer that checks for lease renewal/rebind timeouts.
+ */
+void
+dhcp_coarse_tmr()
+{
+ struct netif *netif = netif_list;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_coarse_tmr()\n"));
+ /* iterate through all network interfaces */
+ while (netif != NULL) {
+ /* only act on DHCP configured interfaces */
+ if (netif->dhcp != NULL) {
+ /* compare lease time to expire timeout */
+ if (++netif->dhcp->lease_used == netif->dhcp->t0_timeout) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_coarse_tmr(): t0 timeout\n"));
+ /* this clients' lease time has expired */
+ dhcp_release(netif);
+ dhcp_discover(netif);
+ /* timer is active (non zero), and triggers (zeroes) now? */
+ } else if (netif->dhcp->t2_rebind_time-- == 1) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_coarse_tmr(): t2 timeout\n"));
+ /* this clients' rebind timeout triggered */
+ dhcp_t2_timeout(netif);
+ /* timer is active (non zero), and triggers (zeroes) now */
+ } else if (netif->dhcp->t1_renew_time-- == 1) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_coarse_tmr(): t1 timeout\n"));
+ /* this clients' renewal timeout triggered */
+ dhcp_t1_timeout(netif);
+ }
+ }
+ /* proceed to next netif */
+ netif = netif->next;
+ }
+}
+
+/**
+ * DHCP transaction timeout handling
+ *
+ * A DHCP server is expected to respond within a short period of time.
+ * This timer checks whether an outstanding DHCP request is timed out.
+ */
+void
+dhcp_fine_tmr()
+{
+ struct netif *netif = netif_list;
+ /* loop through netif's */
+ while (netif != NULL) {
+ /* only act on DHCP configured interfaces */
+ if (netif->dhcp != NULL) {
+ /* timer is active (non zero), and is about to trigger now */
+ if (netif->dhcp->request_timeout > 1) {
+ netif->dhcp->request_timeout--;
+ }
+ else if (netif->dhcp->request_timeout == 1) {
+ netif->dhcp->request_timeout--;
+ /* { netif->dhcp->request_timeout == 0 } */
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_fine_tmr(): request timeout\n"));
+ /* this client's request timeout triggered */
+ dhcp_timeout(netif);
+ }
+ }
+ /* proceed to next network interface */
+ netif = netif->next;
+ }
+}
+
+/**
+ * A DHCP negotiation transaction, or ARP request, has timed out.
+ *
+ * The timer that was started with the DHCP or ARP request has
+ * timed out, indicating no response was received in time.
+ *
+ * @param netif the netif under DHCP control
+ */
+static void
+dhcp_timeout(struct netif *netif)
+{
+ struct dhcp *dhcp = netif->dhcp;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_timeout()\n"));
+ /* back-off period has passed, or server selection timed out */
+ if ((dhcp->state == DHCP_BACKING_OFF) || (dhcp->state == DHCP_SELECTING)) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_timeout(): restarting discovery\n"));
+ dhcp_discover(netif);
+ /* receiving the requested lease timed out */
+ } else if (dhcp->state == DHCP_REQUESTING) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): REQUESTING, DHCP request timed out\n"));
+ if (dhcp->tries <= 5) {
+ dhcp_select(netif);
+ } else {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): REQUESTING, releasing, restarting\n"));
+ dhcp_release(netif);
+ dhcp_discover(netif);
+ }
+#if DHCP_DOES_ARP_CHECK
+ /* received no ARP reply for the offered address (which is good) */
+ } else if (dhcp->state == DHCP_CHECKING) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): CHECKING, ARP request timed out\n"));
+ if (dhcp->tries <= 1) {
+ dhcp_check(netif);
+ /* no ARP replies on the offered address,
+ looks like the IP address is indeed free */
+ } else {
+ /* bind the interface to the offered address */
+ dhcp_bind(netif);
+ }
+#endif /* DHCP_DOES_ARP_CHECK */
+ } else if (dhcp->state == DHCP_REBOOTING) {
+ if (dhcp->tries < REBOOT_TRIES) {
+ dhcp_reboot(netif);
+ } else {
+ dhcp_discover(netif);
+ }
+ }
+}
+
+/**
+ * The renewal period has timed out.
+ *
+ * @param netif the netif under DHCP control
+ */
+static void
+dhcp_t1_timeout(struct netif *netif)
+{
+ struct dhcp *dhcp = netif->dhcp;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_t1_timeout()\n"));
+ if ((dhcp->state == DHCP_REQUESTING) || (dhcp->state == DHCP_BOUND) ||
+ (dhcp->state == DHCP_RENEWING)) {
+ /* just retry to renew - note that the rebind timer (t2) will
+ * eventually time-out if renew tries fail. */
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+ ("dhcp_t1_timeout(): must renew\n"));
+ /* This slightly different to RFC2131: DHCPREQUEST will be sent from state
+ DHCP_RENEWING, not DHCP_BOUND */
+ dhcp_renew(netif);
+ /* Calculate next timeout */
+ if (((netif->dhcp->t2_timeout - dhcp->lease_used) / 2) >= ((60 + DHCP_COARSE_TIMER_SECS / 2) / DHCP_COARSE_TIMER_SECS))
+ {
+ netif->dhcp->t1_renew_time = ((netif->dhcp->t2_timeout - dhcp->lease_used) / 2);
+ }
+ }
+}
+
+/**
+ * The rebind period has timed out.
+ *
+ * @param netif the netif under DHCP control
+ */
+static void
+dhcp_t2_timeout(struct netif *netif)
+{
+ struct dhcp *dhcp = netif->dhcp;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_t2_timeout()\n"));
+ if ((dhcp->state == DHCP_REQUESTING) || (dhcp->state == DHCP_BOUND) ||
+ (dhcp->state == DHCP_RENEWING) || (dhcp->state == DHCP_REBINDING)) {
+ /* just retry to rebind */
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+ ("dhcp_t2_timeout(): must rebind\n"));
+ /* This slightly different to RFC2131: DHCPREQUEST will be sent from state
+ DHCP_REBINDING, not DHCP_BOUND */
+ dhcp_rebind(netif);
+ /* Calculate next timeout */
+ if (((netif->dhcp->t0_timeout - dhcp->lease_used) / 2) >= ((60 + DHCP_COARSE_TIMER_SECS / 2) / DHCP_COARSE_TIMER_SECS))
+ {
+ netif->dhcp->t2_rebind_time = ((netif->dhcp->t0_timeout - dhcp->lease_used) / 2);
+ }
+ }
+}
+
+/**
+ * Handle a DHCP ACK packet
+ *
+ * @param netif the netif under DHCP control
+ */
+static void
+dhcp_handle_ack(struct netif *netif)
+{
+ struct dhcp *dhcp = netif->dhcp;
+#if LWIP_DNS || LWIP_DHCP_GET_NTP_SRV
+ u8_t n;
+#endif /* LWIP_DNS || LWIP_DHCP_GET_NTP_SRV */
+#if LWIP_DHCP_GET_NTP_SRV
+ ip_addr_t ntp_server_addrs[LWIP_DHCP_MAX_NTP_SERVERS];
+#endif
+
+ /* clear options we might not get from the ACK */
+ ip_addr_set_zero(&dhcp->offered_sn_mask);
+ ip_addr_set_zero(&dhcp->offered_gw_addr);
+#if LWIP_DHCP_BOOTP_FILE
+ ip_addr_set_zero(&dhcp->offered_si_addr);
+#endif /* LWIP_DHCP_BOOTP_FILE */
+
+ /* lease time given? */
+ if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_LEASE_TIME)) {
+ /* remember offered lease time */
+ dhcp->offered_t0_lease = dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_LEASE_TIME);
+ }
+ /* renewal period given? */
+ if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_T1)) {
+ /* remember given renewal period */
+ dhcp->offered_t1_renew = dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_T1);
+ } else {
+ /* calculate safe periods for renewal */
+ dhcp->offered_t1_renew = dhcp->offered_t0_lease / 2;
+ }
+
+ /* renewal period given? */
+ if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_T2)) {
+ /* remember given rebind period */
+ dhcp->offered_t2_rebind = dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_T2);
+ } else {
+ /* calculate safe periods for rebinding (offered_t0_lease * 0.875 -> 87.5%)*/
+ dhcp->offered_t2_rebind = (dhcp->offered_t0_lease * 7U) / 8U;
+ }
+
+ /* (y)our internet address */
+ ip_addr_copy(dhcp->offered_ip_addr, dhcp->msg_in->yiaddr);
+
+#if LWIP_DHCP_BOOTP_FILE
+ /* copy boot server address,
+ boot file name copied in dhcp_parse_reply if not overloaded */
+ ip_addr_copy(dhcp->offered_si_addr, dhcp->msg_in->siaddr);
+#endif /* LWIP_DHCP_BOOTP_FILE */
+
+ /* subnet mask given? */
+ if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_SUBNET_MASK)) {
+ /* remember given subnet mask */
+ ip4_addr_set_u32(&dhcp->offered_sn_mask, htonl(dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_SUBNET_MASK)));
+ dhcp->subnet_mask_given = 1;
+ } else {
+ dhcp->subnet_mask_given = 0;
+ }
+
+ /* gateway router */
+ if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_ROUTER)) {
+ ip4_addr_set_u32(&dhcp->offered_gw_addr, htonl(dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_ROUTER)));
+ }
+
+#if LWIP_DHCP_GET_NTP_SRV
+ /* NTP servers */
+ for(n = 0; (n < LWIP_DHCP_MAX_NTP_SERVERS) && dhcp_option_given(dhcp, DHCP_OPTION_IDX_NTP_SERVER + n); n++) {
+ ip4_addr_set_u32(&ntp_server_addrs[n], htonl(dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_NTP_SERVER + n)));
+ }
+ dhcp_set_ntp_servers(n, ntp_server_addrs);
+#endif /* LWIP_DHCP_GET_NTP_SRV */
+
+#if LWIP_DNS
+ /* DNS servers */
+ for(n = 0; (n < DNS_MAX_SERVERS) && dhcp_option_given(dhcp, DHCP_OPTION_IDX_DNS_SERVER + n); n++) {
+ ip_addr_t dns_addr;
+ ip4_addr_set_u32(&dns_addr, htonl(dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_DNS_SERVER + n)));
+ dns_setserver(n, &dns_addr);
+ }
+#endif /* LWIP_DNS */
+}
+
+/** Set a statically allocated struct dhcp to work with.
+ * Using this prevents dhcp_start to allocate it using mem_malloc.
+ *
+ * @param netif the netif for which to set the struct dhcp
+ * @param dhcp (uninitialised) dhcp struct allocated by the application
+ */
+void
+dhcp_set_struct(struct netif *netif, struct dhcp *dhcp)
+{
+ LWIP_ASSERT("netif != NULL", netif != NULL);
+ LWIP_ASSERT("dhcp != NULL", dhcp != NULL);
+ LWIP_ASSERT("netif already has a struct dhcp set", netif->dhcp == NULL);
+
+ /* clear data structure */
+ memset(dhcp, 0, sizeof(struct dhcp));
+ /* dhcp_set_state(&dhcp, DHCP_OFF); */
+ netif->dhcp = dhcp;
+}
+
+/** Removes a struct dhcp from a netif.
+ *
+ * ATTENTION: Only use this when not using dhcp_set_struct() to allocate the
+ * struct dhcp since the memory is passed back to the heap.
+ *
+ * @param netif the netif from which to remove the struct dhcp
+ */
+void dhcp_cleanup(struct netif *netif)
+{
+ LWIP_ASSERT("netif != NULL", netif != NULL);
+
+ if (netif->dhcp != NULL) {
+ mem_free(netif->dhcp);
+ netif->dhcp = NULL;
+ }
+}
+
+/**
+ * Start DHCP negotiation for a network interface.
+ *
+ * If no DHCP client instance was attached to this interface,
+ * a new client is created first. If a DHCP client instance
+ * was already present, it restarts negotiation.
+ *
+ * @param netif The lwIP network interface
+ * @return lwIP error code
+ * - ERR_OK - No error
+ * - ERR_MEM - Out of memory
+ */
+err_t
+dhcp_start(struct netif *netif)
+{
+ struct dhcp *dhcp;
+ err_t result;
+
+ LWIP_ERROR("netif != NULL", (netif != NULL), return ERR_ARG;);
+ LWIP_ERROR("netif is not up, old style port?", netif_is_up(netif), return ERR_ARG;);
+ dhcp = netif->dhcp;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_start(netif=%p) %c%c%"U16_F"\n", (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num));
+
+ /* check hwtype of the netif */
+ if ((netif->flags & NETIF_FLAG_ETHARP) == 0) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): No ETHARP netif\n"));
+ return ERR_ARG;
+ }
+
+ /* check MTU of the netif */
+ if (netif->mtu < DHCP_MAX_MSG_LEN_MIN_REQUIRED) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): Cannot use this netif with DHCP: MTU is too small\n"));
+ return ERR_MEM;
+ }
+
+ /* no DHCP client attached yet? */
+ if (dhcp == NULL) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): starting new DHCP client\n"));
+ dhcp = (struct dhcp *)mem_malloc(sizeof(struct dhcp));
+ if (dhcp == NULL) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): could not allocate dhcp\n"));
+ return ERR_MEM;
+ }
+ /* store this dhcp client in the netif */
+ netif->dhcp = dhcp;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): allocated dhcp"));
+ /* already has DHCP client attached */
+ } else {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_start(): restarting DHCP configuration\n"));
+ if (dhcp->pcb != NULL) {
+ udp_remove(dhcp->pcb);
+ }
+ LWIP_ASSERT("pbuf p_out wasn't freed", dhcp->p_out == NULL);
+ LWIP_ASSERT("reply wasn't freed", dhcp->msg_in == NULL );
+ }
+
+ /* clear data structure */
+ memset(dhcp, 0, sizeof(struct dhcp));
+ /* dhcp_set_state(&dhcp, DHCP_OFF); */
+ /* allocate UDP PCB */
+ dhcp->pcb = udp_new();
+ if (dhcp->pcb == NULL) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): could not obtain pcb\n"));
+ return ERR_MEM;
+ }
+ ip_set_option(dhcp->pcb, SOF_BROADCAST);
+ /* set up local and remote port for the pcb */
+ udp_bind(dhcp->pcb, IP_ADDR_ANY, DHCP_CLIENT_PORT);
+ udp_connect(dhcp->pcb, IP_ADDR_ANY, DHCP_SERVER_PORT);
+ /* set up the recv callback and argument */
+ udp_recv(dhcp->pcb, dhcp_recv, netif);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): starting DHCP configuration\n"));
+
+#if LWIP_DHCP_CHECK_LINK_UP
+ if (!netif_is_link_up(netif)) {
+ /* set state INIT and wait for dhcp_network_changed() to call dhcp_discover() */
+ dhcp_set_state(dhcp, DHCP_INIT);
+ return ERR_OK;
+ }
+#endif /* LWIP_DHCP_CHECK_LINK_UP */
+
+ /* (re)start the DHCP negotiation */
+ result = dhcp_discover(netif);
+ if (result != ERR_OK) {
+ /* free resources allocated above */
+ dhcp_stop(netif);
+ return ERR_MEM;
+ }
+ return result;
+}
+
+/**
+ * Inform a DHCP server of our manual configuration.
+ *
+ * This informs DHCP servers of our fixed IP address configuration
+ * by sending an INFORM message. It does not involve DHCP address
+ * configuration, it is just here to be nice to the network.
+ *
+ * @param netif The lwIP network interface
+ */
+void
+dhcp_inform(struct netif *netif)
+{
+ struct dhcp dhcp;
+ err_t result = ERR_OK;
+ struct udp_pcb *pcb;
+
+ LWIP_ERROR("netif != NULL", (netif != NULL), return;);
+
+ memset(&dhcp, 0, sizeof(struct dhcp));
+ dhcp_set_state(&dhcp, DHCP_INFORM);
+
+ if ((netif->dhcp != NULL) && (netif->dhcp->pcb != NULL)) {
+ /* re-use existing pcb */
+ pcb = netif->dhcp->pcb;
+ } else {
+ pcb = udp_new();
+ if (pcb == NULL) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_inform(): could not obtain pcb"));
+ return;
+ }
+ dhcp.pcb = pcb;
+ ip_set_option(dhcp.pcb, SOF_BROADCAST);
+ udp_bind(dhcp.pcb, IP_ADDR_ANY, DHCP_CLIENT_PORT);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_inform(): created new udp pcb\n"));
+ }
+ /* create and initialize the DHCP message header */
+ result = dhcp_create_msg(netif, &dhcp, DHCP_INFORM);
+ if (result == ERR_OK) {
+ dhcp_option(&dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
+ dhcp_option_short(&dhcp, DHCP_MAX_MSG_LEN(netif));
+
+ dhcp_option_trailer(&dhcp);
+
+ pbuf_realloc(dhcp.p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp.options_out_len);
+
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_inform: INFORMING\n"));
+ udp_sendto_if(pcb, dhcp.p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif);
+ dhcp_delete_msg(&dhcp);
+ } else {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_inform: could not allocate DHCP request\n"));
+ }
+
+ if (dhcp.pcb != NULL) {
+ /* otherwise, the existing pcb was used */
+ udp_remove(dhcp.pcb);
+ }
+}
+
+/** Handle a possible change in the network configuration.
+ *
+ * This enters the REBOOTING state to verify that the currently bound
+ * address is still valid.
+ */
+void
+dhcp_network_changed(struct netif *netif)
+{
+ struct dhcp *dhcp = netif->dhcp;
+ if (!dhcp)
+ return;
+ switch (dhcp->state) {
+ case DHCP_REBINDING:
+ case DHCP_RENEWING:
+ case DHCP_BOUND:
+ case DHCP_REBOOTING:
+ dhcp->tries = 0;
+ dhcp_reboot(netif);
+ break;
+ case DHCP_OFF:
+ /* stay off */
+ break;
+ default:
+ /* INIT/REQUESTING/CHECKING/BACKING_OFF restart with new 'rid' because the
+ state changes, SELECTING: continue with current 'rid' as we stay in the
+ same state */
+#if LWIP_DHCP_AUTOIP_COOP
+ if(dhcp->autoip_coop_state == DHCP_AUTOIP_COOP_STATE_ON) {
+ autoip_stop(netif);
+ dhcp->autoip_coop_state = DHCP_AUTOIP_COOP_STATE_OFF;
+ }
+#endif /* LWIP_DHCP_AUTOIP_COOP */
+ dhcp_discover(netif);
+ break;
+ }
+}
+
+#if DHCP_DOES_ARP_CHECK
+/**
+ * Match an ARP reply with the offered IP address.
+ *
+ * @param netif the network interface on which the reply was received
+ * @param addr The IP address we received a reply from
+ */
+void dhcp_arp_reply(struct netif *netif, ip_addr_t *addr)
+{
+ LWIP_ERROR("netif != NULL", (netif != NULL), return;);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_arp_reply()\n"));
+ /* is a DHCP client doing an ARP check? */
+ if ((netif->dhcp != NULL) && (netif->dhcp->state == DHCP_CHECKING)) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_arp_reply(): CHECKING, arp reply for 0x%08"X32_F"\n",
+ ip4_addr_get_u32(addr)));
+ /* did a host respond with the address we
+ were offered by the DHCP server? */
+ if (ip_addr_cmp(addr, &netif->dhcp->offered_ip_addr)) {
+ /* we will not accept the offered address */
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | LWIP_DBG_LEVEL_WARNING,
+ ("dhcp_arp_reply(): arp reply matched with offered address, declining\n"));
+ dhcp_decline(netif);
+ }
+ }
+}
+
+/**
+ * Decline an offered lease.
+ *
+ * Tell the DHCP server we do not accept the offered address.
+ * One reason to decline the lease is when we find out the address
+ * is already in use by another host (through ARP).
+ *
+ * @param netif the netif under DHCP control
+ */
+static err_t
+dhcp_decline(struct netif *netif)
+{
+ struct dhcp *dhcp = netif->dhcp;
+ err_t result = ERR_OK;
+ u16_t msecs;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_decline()\n"));
+ dhcp_set_state(dhcp, DHCP_BACKING_OFF);
+ /* create and initialize the DHCP message header */
+ result = dhcp_create_msg(netif, dhcp, DHCP_DECLINE);
+ if (result == ERR_OK) {
+ dhcp_option(dhcp, DHCP_OPTION_REQUESTED_IP, 4);
+ dhcp_option_long(dhcp, ntohl(ip4_addr_get_u32(&dhcp->offered_ip_addr)));
+
+ dhcp_option_trailer(dhcp);
+ /* resize pbuf to reflect true size of options */
+ pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len);
+
+ /* per section 4.4.4, broadcast DECLINE messages */
+ udp_sendto_if_src(dhcp->pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif, IP_ADDR_ANY);
+ dhcp_delete_msg(dhcp);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_decline: BACKING OFF\n"));
+ } else {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
+ ("dhcp_decline: could not allocate DHCP request\n"));
+ }
+ dhcp->tries++;
+ msecs = 10*1000;
+ dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_decline(): set request timeout %"U16_F" msecs\n", msecs));
+ return result;
+}
+#endif /* DHCP_DOES_ARP_CHECK */
+
+
+/**
+ * Start the DHCP process, discover a DHCP server.
+ *
+ * @param netif the netif under DHCP control
+ */
+static err_t
+dhcp_discover(struct netif *netif)
+{
+ struct dhcp *dhcp = netif->dhcp;
+ err_t result = ERR_OK;
+ u16_t msecs;
+ u8_t i;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover()\n"));
+ ip_addr_set_any(&dhcp->offered_ip_addr);
+ dhcp_set_state(dhcp, DHCP_SELECTING);
+ /* create and initialize the DHCP message header */
+ result = dhcp_create_msg(netif, dhcp, DHCP_DISCOVER);
+ if (result == ERR_OK) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover: making request\n"));
+
+ dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
+ dhcp_option_short(dhcp, DHCP_MAX_MSG_LEN(netif));
+
+ dhcp_option(dhcp, DHCP_OPTION_PARAMETER_REQUEST_LIST, sizeof(dhcp_discover_select_options));
+ for (i = 0; i < sizeof(dhcp_discover_select_options); i++) {
+ dhcp_option_byte(dhcp, dhcp_discover_select_options[i]);
+ }
+ dhcp_option_trailer(dhcp);
+
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover: realloc()ing\n"));
+ pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len);
+
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover: sendto(DISCOVER, IP_ADDR_BROADCAST, DHCP_SERVER_PORT)\n"));
+ udp_sendto_if_src(dhcp->pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif, IP_ADDR_ANY);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover: deleting()ing\n"));
+ dhcp_delete_msg(dhcp);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_discover: SELECTING\n"));
+ } else {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_discover: could not allocate DHCP request\n"));
+ }
+ dhcp->tries++;
+#if LWIP_DHCP_AUTOIP_COOP
+ if(dhcp->tries >= LWIP_DHCP_AUTOIP_COOP_TRIES && dhcp->autoip_coop_state == DHCP_AUTOIP_COOP_STATE_OFF) {
+ dhcp->autoip_coop_state = DHCP_AUTOIP_COOP_STATE_ON;
+ autoip_start(netif);
+ }
+#endif /* LWIP_DHCP_AUTOIP_COOP */
+ msecs = (dhcp->tries < 6 ? 1 << dhcp->tries : 60) * 1000;
+ dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_discover(): set request timeout %"U16_F" msecs\n", msecs));
+ return result;
+}
+
+
+/**
+ * Bind the interface to the offered IP address.
+ *
+ * @param netif network interface to bind to the offered address
+ */
+static void
+dhcp_bind(struct netif *netif)
+{
+ u32_t timeout;
+ struct dhcp *dhcp;
+ ip_addr_t sn_mask, gw_addr;
+ LWIP_ERROR("dhcp_bind: netif != NULL", (netif != NULL), return;);
+ dhcp = netif->dhcp;
+ LWIP_ERROR("dhcp_bind: dhcp != NULL", (dhcp != NULL), return;);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_bind(netif=%p) %c%c%"U16_F"\n", (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num));
+
+ /* reset time used of lease */
+ dhcp->lease_used = 0;
+
+ if (dhcp->offered_t0_lease != 0xffffffffUL) {
+ /* set renewal period timer */
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_bind(): t0 renewal timer %"U32_F" secs\n", dhcp->offered_t0_lease));
+ timeout = (dhcp->offered_t0_lease + DHCP_COARSE_TIMER_SECS / 2) / DHCP_COARSE_TIMER_SECS;
+ if(timeout > 0xffff) {
+ timeout = 0xffff;
+ }
+ dhcp->t0_timeout = (u16_t)timeout;
+ if (dhcp->t0_timeout == 0) {
+ dhcp->t0_timeout = 1;
+ }
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_bind(): set request timeout %"U32_F" msecs\n", dhcp->offered_t0_lease*1000));
+ }
+
+ /* temporary DHCP lease? */
+ if (dhcp->offered_t1_renew != 0xffffffffUL) {
+ /* set renewal period timer */
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_bind(): t1 renewal timer %"U32_F" secs\n", dhcp->offered_t1_renew));
+ timeout = (dhcp->offered_t1_renew + DHCP_COARSE_TIMER_SECS / 2) / DHCP_COARSE_TIMER_SECS;
+ if(timeout > 0xffff) {
+ timeout = 0xffff;
+ }
+ dhcp->t1_timeout = (u16_t)timeout;
+ if (dhcp->t1_timeout == 0) {
+ dhcp->t1_timeout = 1;
+ }
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_bind(): set request timeout %"U32_F" msecs\n", dhcp->offered_t1_renew*1000));
+ dhcp->t1_renew_time = dhcp->t1_timeout;
+ }
+ /* set renewal period timer */
+ if (dhcp->offered_t2_rebind != 0xffffffffUL) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_bind(): t2 rebind timer %"U32_F" secs\n", dhcp->offered_t2_rebind));
+ timeout = (dhcp->offered_t2_rebind + DHCP_COARSE_TIMER_SECS / 2) / DHCP_COARSE_TIMER_SECS;
+ if(timeout > 0xffff) {
+ timeout = 0xffff;
+ }
+ dhcp->t2_timeout = (u16_t)timeout;
+ if (dhcp->t2_timeout == 0) {
+ dhcp->t2_timeout = 1;
+ }
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_bind(): set request timeout %"U32_F" msecs\n", dhcp->offered_t2_rebind*1000));
+ dhcp->t2_rebind_time = dhcp->t2_timeout;
+ }
+
+ /* If we have sub 1 minute lease, t2 and t1 will kick in at the same time. */
+ if ((dhcp->t1_timeout >= dhcp->t2_timeout) && (dhcp->t2_timeout > 0)) {
+ dhcp->t1_timeout = 0;
+ }
+
+ if (dhcp->subnet_mask_given) {
+ /* copy offered network mask */
+ ip_addr_copy(sn_mask, dhcp->offered_sn_mask);
+ } else {
+ /* subnet mask not given, choose a safe subnet mask given the network class */
+ u8_t first_octet = ip4_addr1(&dhcp->offered_ip_addr);
+ if (first_octet <= 127) {
+ ip4_addr_set_u32(&sn_mask, PP_HTONL(0xff000000UL));
+ } else if (first_octet >= 192) {
+ ip4_addr_set_u32(&sn_mask, PP_HTONL(0xffffff00UL));
+ } else {
+ ip4_addr_set_u32(&sn_mask, PP_HTONL(0xffff0000UL));
+ }
+ }
+
+ ip_addr_copy(gw_addr, dhcp->offered_gw_addr);
+ /* gateway address not given? */
+ if (ip_addr_isany(&gw_addr)) {
+ /* copy network address */
+ ip_addr_get_network(&gw_addr, &dhcp->offered_ip_addr, &sn_mask);
+ /* use first host address on network as gateway */
+ ip4_addr_set_u32(&gw_addr, ip4_addr_get_u32(&gw_addr) | PP_HTONL(0x00000001UL));
+ }
+
+#if LWIP_DHCP_AUTOIP_COOP
+ if(dhcp->autoip_coop_state == DHCP_AUTOIP_COOP_STATE_ON) {
+ autoip_stop(netif);
+ dhcp->autoip_coop_state = DHCP_AUTOIP_COOP_STATE_OFF;
+ }
+#endif /* LWIP_DHCP_AUTOIP_COOP */
+
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_bind(): IP: 0x%08"X32_F" SN: 0x%08"X32_F" GW: 0x%08"X32_F"\n",
+ ip4_addr_get_u32(&dhcp->offered_ip_addr), ip4_addr_get_u32(&sn_mask), ip4_addr_get_u32(&gw_addr)));
+ netif_set_addr(netif, &dhcp->offered_ip_addr, &sn_mask, &gw_addr);
+ /* interface is used by routing now that an address is set */
+
+ /* netif is now bound to DHCP leased address */
+ dhcp_set_state(dhcp, DHCP_BOUND);
+}
+
+/**
+ * Renew an existing DHCP lease at the involved DHCP server.
+ *
+ * @param netif network interface which must renew its lease
+ */
+err_t
+dhcp_renew(struct netif *netif)
+{
+ struct dhcp *dhcp = netif->dhcp;
+ err_t result;
+ u16_t msecs;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_renew()\n"));
+ dhcp_set_state(dhcp, DHCP_RENEWING);
+
+ /* create and initialize the DHCP message header */
+ result = dhcp_create_msg(netif, dhcp, DHCP_REQUEST);
+ if (result == ERR_OK) {
+ dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
+ dhcp_option_short(dhcp, DHCP_MAX_MSG_LEN(netif));
+
+#if LWIP_NETIF_HOSTNAME
+ dhcp_option_hostname(dhcp, netif);
+#endif /* LWIP_NETIF_HOSTNAME */
+
+ /* append DHCP message trailer */
+ dhcp_option_trailer(dhcp);
+
+ pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len);
+
+ udp_sendto_if(dhcp->pcb, dhcp->p_out, &dhcp->server_ip_addr, DHCP_SERVER_PORT, netif);
+ dhcp_delete_msg(dhcp);
+
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_renew: RENEWING\n"));
+ } else {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_renew: could not allocate DHCP request\n"));
+ }
+ dhcp->tries++;
+ /* back-off on retries, but to a maximum of 20 seconds */
+ msecs = dhcp->tries < 10 ? dhcp->tries * 2000 : 20 * 1000;
+ dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_renew(): set request timeout %"U16_F" msecs\n", msecs));
+ return result;
+}
+
+/**
+ * Rebind with a DHCP server for an existing DHCP lease.
+ *
+ * @param netif network interface which must rebind with a DHCP server
+ */
+static err_t
+dhcp_rebind(struct netif *netif)
+{
+ struct dhcp *dhcp = netif->dhcp;
+ err_t result;
+ u16_t msecs;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_rebind()\n"));
+ dhcp_set_state(dhcp, DHCP_REBINDING);
+
+ /* create and initialize the DHCP message header */
+ result = dhcp_create_msg(netif, dhcp, DHCP_REQUEST);
+ if (result == ERR_OK) {
+ dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
+ dhcp_option_short(dhcp, DHCP_MAX_MSG_LEN(netif));
+
+#if LWIP_NETIF_HOSTNAME
+ dhcp_option_hostname(dhcp, netif);
+#endif /* LWIP_NETIF_HOSTNAME */
+
+ dhcp_option_trailer(dhcp);
+
+ pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len);
+
+ /* broadcast to server */
+ udp_sendto_if(dhcp->pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif);
+ dhcp_delete_msg(dhcp);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_rebind: REBINDING\n"));
+ } else {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_rebind: could not allocate DHCP request\n"));
+ }
+ dhcp->tries++;
+ msecs = dhcp->tries < 10 ? dhcp->tries * 1000 : 10 * 1000;
+ dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_rebind(): set request timeout %"U16_F" msecs\n", msecs));
+ return result;
+}
+
+/**
+ * Enter REBOOTING state to verify an existing lease
+ *
+ * @param netif network interface which must reboot
+ */
+static err_t
+dhcp_reboot(struct netif *netif)
+{
+ struct dhcp *dhcp = netif->dhcp;
+ err_t result;
+ u16_t msecs;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_reboot()\n"));
+ dhcp_set_state(dhcp, DHCP_REBOOTING);
+
+ /* create and initialize the DHCP message header */
+ result = dhcp_create_msg(netif, dhcp, DHCP_REQUEST);
+ if (result == ERR_OK) {
+ dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
+ dhcp_option_short(dhcp, 576);
+
+ dhcp_option(dhcp, DHCP_OPTION_REQUESTED_IP, 4);
+ dhcp_option_long(dhcp, ntohl(ip4_addr_get_u32(&dhcp->offered_ip_addr)));
+
+ dhcp_option_trailer(dhcp);
+
+ pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len);
+
+ /* broadcast to server */
+ udp_sendto_if(dhcp->pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif);
+ dhcp_delete_msg(dhcp);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_reboot: REBOOTING\n"));
+ } else {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_reboot: could not allocate DHCP request\n"));
+ }
+ dhcp->tries++;
+ msecs = dhcp->tries < 10 ? dhcp->tries * 1000 : 10 * 1000;
+ dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_reboot(): set request timeout %"U16_F" msecs\n", msecs));
+ return result;
+}
+
+
+/**
+ * Release a DHCP lease.
+ *
+ * @param netif network interface which must release its lease
+ */
+err_t
+dhcp_release(struct netif *netif)
+{
+ struct dhcp *dhcp = netif->dhcp;
+ err_t result;
+ u16_t msecs;
+ ip_addr_t server_ip_addr;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_release()\n"));
+ if (dhcp == NULL) {
+ return ERR_ARG;
+ }
+ ip_addr_copy(server_ip_addr, dhcp->server_ip_addr);
+
+ /* idle DHCP client */
+ dhcp_set_state(dhcp, DHCP_OFF);
+ /* clean old DHCP offer */
+ ip_addr_set_zero(&dhcp->server_ip_addr);
+ ip_addr_set_zero(&dhcp->offered_ip_addr);
+ ip_addr_set_zero(&dhcp->offered_sn_mask);
+ ip_addr_set_zero(&dhcp->offered_gw_addr);
+#if LWIP_DHCP_BOOTP_FILE
+ ip_addr_set_zero(&dhcp->offered_si_addr);
+#endif /* LWIP_DHCP_BOOTP_FILE */
+ dhcp->offered_t0_lease = dhcp->offered_t1_renew = dhcp->offered_t2_rebind = 0;
+
+ /* create and initialize the DHCP message header */
+ result = dhcp_create_msg(netif, dhcp, DHCP_RELEASE);
+ if (result == ERR_OK) {
+ dhcp_option(dhcp, DHCP_OPTION_SERVER_ID, 4);
+ dhcp_option_long(dhcp, ntohl(ip4_addr_get_u32(&server_ip_addr)));
+
+ dhcp_option_trailer(dhcp);
+
+ pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len);
+
+ udp_sendto_if(dhcp->pcb, dhcp->p_out, &server_ip_addr, DHCP_SERVER_PORT, netif);
+ dhcp_delete_msg(dhcp);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_release: RELEASED, DHCP_OFF\n"));
+ } else {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_release: could not allocate DHCP request\n"));
+ }
+ dhcp->tries++;
+ msecs = dhcp->tries < 10 ? dhcp->tries * 1000 : 10 * 1000;
+ dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_release(): set request timeout %"U16_F" msecs\n", msecs));
+ /* remove IP address from interface (prevents routing from selecting this interface) */
+ netif_set_addr(netif, IP_ADDR_ANY, IP_ADDR_ANY, IP_ADDR_ANY);
+
+ return result;
+}
+
+/**
+ * Remove the DHCP client from the interface.
+ *
+ * @param netif The network interface to stop DHCP on
+ */
+void
+dhcp_stop(struct netif *netif)
+{
+ struct dhcp *dhcp;
+ LWIP_ERROR("dhcp_stop: netif != NULL", (netif != NULL), return;);
+ dhcp = netif->dhcp;
+
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_stop()\n"));
+ /* netif is DHCP configured? */
+ if (dhcp != NULL) {
+#if LWIP_DHCP_AUTOIP_COOP
+ if(dhcp->autoip_coop_state == DHCP_AUTOIP_COOP_STATE_ON) {
+ autoip_stop(netif);
+ dhcp->autoip_coop_state = DHCP_AUTOIP_COOP_STATE_OFF;
+ }
+#endif /* LWIP_DHCP_AUTOIP_COOP */
+
+ if (dhcp->pcb != NULL) {
+ udp_remove(dhcp->pcb);
+ dhcp->pcb = NULL;
+ }
+ LWIP_ASSERT("reply wasn't freed", dhcp->msg_in == NULL);
+ dhcp_set_state(dhcp, DHCP_OFF);
+ }
+}
+
+/*
+ * Set the DHCP state of a DHCP client.
+ *
+ * If the state changed, reset the number of tries.
+ */
+static void
+dhcp_set_state(struct dhcp *dhcp, u8_t new_state)
+{
+ if (new_state != dhcp->state) {
+ dhcp->state = new_state;
+ dhcp->tries = 0;
+ dhcp->request_timeout = 0;
+ }
+}
+
+/*
+ * Concatenate an option type and length field to the outgoing
+ * DHCP message.
+ *
+ */
+static void
+dhcp_option(struct dhcp *dhcp, u8_t option_type, u8_t option_len)
+{
+ LWIP_ASSERT("dhcp_option: dhcp->options_out_len + 2 + option_len <= DHCP_OPTIONS_LEN", dhcp->options_out_len + 2U + option_len <= DHCP_OPTIONS_LEN);
+ dhcp->msg_out->options[dhcp->options_out_len++] = option_type;
+ dhcp->msg_out->options[dhcp->options_out_len++] = option_len;
+}
+/*
+ * Concatenate a single byte to the outgoing DHCP message.
+ *
+ */
+static void
+dhcp_option_byte(struct dhcp *dhcp, u8_t value)
+{
+ LWIP_ASSERT("dhcp_option_byte: dhcp->options_out_len < DHCP_OPTIONS_LEN", dhcp->options_out_len < DHCP_OPTIONS_LEN);
+ dhcp->msg_out->options[dhcp->options_out_len++] = value;
+}
+
+static void
+dhcp_option_short(struct dhcp *dhcp, u16_t value)
+{
+ LWIP_ASSERT("dhcp_option_short: dhcp->options_out_len + 2 <= DHCP_OPTIONS_LEN", dhcp->options_out_len + 2U <= DHCP_OPTIONS_LEN);
+ dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t)((value & 0xff00U) >> 8);
+ dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t) (value & 0x00ffU);
+}
+
+static void
+dhcp_option_long(struct dhcp *dhcp, u32_t value)
+{
+ LWIP_ASSERT("dhcp_option_long: dhcp->options_out_len + 4 <= DHCP_OPTIONS_LEN", dhcp->options_out_len + 4U <= DHCP_OPTIONS_LEN);
+ dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t)((value & 0xff000000UL) >> 24);
+ dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t)((value & 0x00ff0000UL) >> 16);
+ dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t)((value & 0x0000ff00UL) >> 8);
+ dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t)((value & 0x000000ffUL));
+}
+
+#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.
+ *
+ * Extract the DHCP message and the DHCP options, each into a contiguous
+ * piece of memory. As a DHCP message is variable sized by its options,
+ * and also allows overriding some fields for options, the easy approach
+ * is to first unfold the options into a contiguous piece of memory, and
+ * use that further on.
+ *
+ */
+static err_t
+dhcp_parse_reply(struct dhcp *dhcp, struct pbuf *p)
+{
+ u8_t *options;
+ u16_t offset;
+ u16_t offset_max;
+ u16_t options_idx;
+ u16_t options_idx_max;
+ struct pbuf *q;
+ int parse_file_as_options = 0;
+ int parse_sname_as_options = 0;
+
+ /* clear received options */
+ dhcp_clear_all_options(dhcp);
+ /* check that beginning of dhcp_msg (up to and including chaddr) is in first pbuf */
+ if (p->len < DHCP_SNAME_OFS) {
+ return ERR_BUF;
+ }
+ dhcp->msg_in = (struct dhcp_msg *)p->payload;
+#if LWIP_DHCP_BOOTP_FILE
+ /* clear boot file name */
+ dhcp->boot_file_name[0] = 0;
+#endif /* LWIP_DHCP_BOOTP_FILE */
+
+ /* parse options */
+
+ /* start with options field */
+ options_idx = DHCP_OPTIONS_OFS;
+ /* parse options to the end of the received packet */
+ options_idx_max = p->tot_len;
+again:
+ q = p;
+ while((q != NULL) && (options_idx >= q->len)) {
+ options_idx -= q->len;
+ options_idx_max -= q->len;
+ q = q->next;
+ }
+ if (q == NULL) {
+ return ERR_BUF;
+ }
+ offset = options_idx;
+ offset_max = options_idx_max;
+ options = (u8_t*)q->payload;
+ /* at least 1 byte to read and no end marker, then at least 3 bytes to read? */
+ while((q != NULL) && (options[offset] != DHCP_OPTION_END) && (offset < offset_max)) {
+ u8_t op = options[offset];
+ u8_t len;
+ u8_t decode_len = 0;
+ int decode_idx = -1;
+ u16_t val_offset = offset + 2;
+ /* len byte might be in the next pbuf */
+ if (offset + 1 < q->len) {
+ len = options[offset + 1];
+ } else {
+ len = (q->next != NULL ? ((u8_t*)q->next->payload)[0] : 0);
+ }
+ /* LWIP_DEBUGF(DHCP_DEBUG, ("msg_offset=%"U16_F", q->len=%"U16_F, msg_offset, q->len)); */
+ decode_len = len;
+ switch(op) {
+ /* case(DHCP_OPTION_END): handled above */
+ case(DHCP_OPTION_PAD):
+ /* special option: no len encoded */
+ decode_len = len = 0;
+ /* will be increased below */
+ offset--;
+ break;
+ case(DHCP_OPTION_SUBNET_MASK):
+ LWIP_ERROR("len == 4", len == 4, return ERR_VAL;);
+ decode_idx = DHCP_OPTION_IDX_SUBNET_MASK;
+ break;
+ case(DHCP_OPTION_ROUTER):
+ decode_len = 4; /* only copy the first given router */
+ LWIP_ERROR("len >= decode_len", len >= decode_len, return ERR_VAL;);
+ decode_idx = DHCP_OPTION_IDX_ROUTER;
+ break;
+ case(DHCP_OPTION_DNS_SERVER):
+ /* special case: there might be more than one server */
+ LWIP_ERROR("len % 4 == 0", len % 4 == 0, return ERR_VAL;);
+ /* limit number of DNS servers */
+ decode_len = LWIP_MIN(len, 4 * DNS_MAX_SERVERS);
+ LWIP_ERROR("len >= decode_len", len >= decode_len, return ERR_VAL;);
+ decode_idx = DHCP_OPTION_IDX_DNS_SERVER;
+ break;
+ case(DHCP_OPTION_LEASE_TIME):
+ LWIP_ERROR("len == 4", len == 4, return ERR_VAL;);
+ decode_idx = DHCP_OPTION_IDX_LEASE_TIME;
+ break;
+#if LWIP_DHCP_GET_NTP_SRV
+ case(DHCP_OPTION_NTP):
+ /* special case: there might be more than one server */
+ LWIP_ERROR("len % 4 == 0", len % 4 == 0, return ERR_VAL;);
+ /* limit number of NTP servers */
+ decode_len = LWIP_MIN(len, 4 * LWIP_DHCP_MAX_NTP_SERVERS);
+ LWIP_ERROR("len >= decode_len", len >= decode_len, return ERR_VAL;);
+ decode_idx = DHCP_OPTION_IDX_NTP_SERVER;
+ break;
+#endif /* LWIP_DHCP_GET_NTP_SRV*/
+ case(DHCP_OPTION_OVERLOAD):
+ LWIP_ERROR("len == 1", len == 1, return ERR_VAL;);
+ decode_idx = DHCP_OPTION_IDX_OVERLOAD;
+ break;
+ case(DHCP_OPTION_MESSAGE_TYPE):
+ LWIP_ERROR("len == 1", len == 1, return ERR_VAL;);
+ decode_idx = DHCP_OPTION_IDX_MSG_TYPE;
+ break;
+ case(DHCP_OPTION_SERVER_ID):
+ LWIP_ERROR("len == 4", len == 4, return ERR_VAL;);
+ decode_idx = DHCP_OPTION_IDX_SERVER_ID;
+ break;
+ case(DHCP_OPTION_T1):
+ LWIP_ERROR("len == 4", len == 4, return ERR_VAL;);
+ decode_idx = DHCP_OPTION_IDX_T1;
+ break;
+ case(DHCP_OPTION_T2):
+ LWIP_ERROR("len == 4", len == 4, return ERR_VAL;);
+ decode_idx = DHCP_OPTION_IDX_T2;
+ break;
+ default:
+ decode_len = 0;
+ LWIP_DEBUGF(DHCP_DEBUG, ("skipping option %"U16_F" in options\n", op));
+ break;
+ }
+ offset += len + 2;
+ if (decode_len > 0) {
+ u32_t value = 0;
+ u16_t copy_len;
+decode_next:
+ LWIP_ASSERT("check decode_idx", decode_idx >= 0 && decode_idx < DHCP_OPTION_IDX_MAX);
+ if (!dhcp_option_given(dhcp, decode_idx)) {
+ copy_len = LWIP_MIN(decode_len, 4);
+ pbuf_copy_partial(q, &value, copy_len, val_offset);
+ if (decode_len > 4) {
+ /* decode more than one u32_t */
+ LWIP_ERROR("decode_len % 4 == 0", decode_len % 4 == 0, return ERR_VAL;);
+ dhcp_got_option(dhcp, decode_idx);
+ dhcp_set_option_value(dhcp, decode_idx, htonl(value));
+ decode_len -= 4;
+ val_offset += 4;
+ decode_idx++;
+ goto decode_next;
+ } else if (decode_len == 4) {
+ value = ntohl(value);
+ } else {
+ LWIP_ERROR("invalid decode_len", decode_len == 1, return ERR_VAL;);
+ value = ((u8_t*)&value)[0];
+ }
+ dhcp_got_option(dhcp, decode_idx);
+ dhcp_set_option_value(dhcp, decode_idx, value);
+ }
+ }
+ if (offset >= q->len) {
+ offset -= q->len;
+ offset_max -= q->len;
+ if ((offset < offset_max) && offset_max) {
+ q = q->next;
+ LWIP_ASSERT("next pbuf was null", q);
+ options = (u8_t*)q->payload;
+ } else {
+ /* We've run out of bytes, probably no end marker. Don't proceed. */
+ break;
+ }
+ }
+ }
+ /* is this an overloaded message? */
+ if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_OVERLOAD)) {
+ u32_t overload = dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_OVERLOAD);
+ dhcp_clear_option(dhcp, DHCP_OPTION_IDX_OVERLOAD);
+ if (overload == DHCP_OVERLOAD_FILE) {
+ parse_file_as_options = 1;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("overloaded file field\n"));
+ } else if (overload == DHCP_OVERLOAD_SNAME) {
+ parse_sname_as_options = 1;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("overloaded sname field\n"));
+ } else if (overload == DHCP_OVERLOAD_SNAME_FILE) {
+ parse_sname_as_options = 1;
+ parse_file_as_options = 1;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("overloaded sname and file field\n"));
+ } else {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("invalid overload option: %d\n", (int)overload));
+ }
+#if LWIP_DHCP_BOOTP_FILE
+ if (!parse_file_as_options) {
+ /* only do this for ACK messages */
+ if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_MSG_TYPE) &&
+ (dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_MSG_TYPE) == DHCP_ACK))
+ /* copy bootp file name, don't care for sname (server hostname) */
+ pbuf_copy_partial(p, dhcp->boot_file_name, DHCP_FILE_LEN-1, DHCP_FILE_OFS);
+ /* make sure the string is really NULL-terminated */
+ dhcp->boot_file_name[DHCP_FILE_LEN-1] = 0;
+ }
+#endif /* LWIP_DHCP_BOOTP_FILE */
+ }
+ if (parse_file_as_options) {
+ /* if both are overloaded, parse file first and then sname (RFC 2131 ch. 4.1) */
+ parse_file_as_options = 0;
+ options_idx = DHCP_FILE_OFS;
+ options_idx_max = DHCP_FILE_OFS + DHCP_FILE_LEN;
+ goto again;
+ } else if (parse_sname_as_options) {
+ parse_sname_as_options = 0;
+ options_idx = DHCP_SNAME_OFS;
+ options_idx_max = DHCP_SNAME_OFS + DHCP_SNAME_LEN;
+ goto again;
+ }
+ return ERR_OK;
+}
+
+/**
+ * If an incoming DHCP message is in response to us, then trigger the state machine
+ */
+static void
+dhcp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
+{
+ struct netif *netif = (struct netif *)arg;
+ struct dhcp *dhcp = netif->dhcp;
+ struct dhcp_msg *reply_msg = (struct dhcp_msg *)p->payload;
+ u8_t msg_type;
+ u8_t i;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_recv(pbuf = %p) from DHCP server %"U16_F".%"U16_F".%"U16_F".%"U16_F" port %"U16_F"\n", (void*)p,
+ ip4_addr1_16(addr), ip4_addr2_16(addr), ip4_addr3_16(addr), ip4_addr4_16(addr), port));
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("pbuf->len = %"U16_F"\n", p->len));
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("pbuf->tot_len = %"U16_F"\n", p->tot_len));
+ /* prevent warnings about unused arguments */
+ LWIP_UNUSED_ARG(pcb);
+ LWIP_UNUSED_ARG(addr);
+ LWIP_UNUSED_ARG(port);
+
+ LWIP_ASSERT("reply wasn't freed", dhcp->msg_in == NULL);
+
+ if (p->len < DHCP_MIN_REPLY_LEN) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("DHCP reply message or pbuf too short\n"));
+ goto free_pbuf_and_return;
+ }
+
+ if (reply_msg->op != DHCP_BOOTREPLY) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("not a DHCP reply message, but type %"U16_F"\n", (u16_t)reply_msg->op));
+ goto free_pbuf_and_return;
+ }
+ /* iterate through hardware address and match against DHCP message */
+ for (i = 0; i < netif->hwaddr_len && i < NETIF_MAX_HWADDR_LEN && i < DHCP_CHADDR_LEN; i++) {
+ if (netif->hwaddr[i] != reply_msg->chaddr[i]) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING,
+ ("netif->hwaddr[%"U16_F"]==%02"X16_F" != reply_msg->chaddr[%"U16_F"]==%02"X16_F"\n",
+ (u16_t)i, (u16_t)netif->hwaddr[i], (u16_t)i, (u16_t)reply_msg->chaddr[i]));
+ goto free_pbuf_and_return;
+ }
+ }
+ /* match transaction ID against what we expected */
+ if (ntohl(reply_msg->xid) != dhcp->xid) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING,
+ ("transaction id mismatch reply_msg->xid(%"X32_F")!=dhcp->xid(%"X32_F")\n",ntohl(reply_msg->xid),dhcp->xid));
+ goto free_pbuf_and_return;
+ }
+ /* option fields could be unfold? */
+ if (dhcp_parse_reply(dhcp, p) != ERR_OK) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
+ ("problem unfolding DHCP message - too short on memory?\n"));
+ goto free_pbuf_and_return;
+ }
+
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("searching DHCP_OPTION_MESSAGE_TYPE\n"));
+ /* obtain pointer to DHCP message type */
+ if (!dhcp_option_given(dhcp, DHCP_OPTION_IDX_MSG_TYPE)) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("DHCP_OPTION_MESSAGE_TYPE option not found\n"));
+ goto free_pbuf_and_return;
+ }
+
+ /* read DHCP message type */
+ msg_type = (u8_t)dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_MSG_TYPE);
+ /* message type is DHCP ACK? */
+ if (msg_type == DHCP_ACK) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("DHCP_ACK received\n"));
+ /* in requesting state? */
+ if (dhcp->state == DHCP_REQUESTING) {
+ dhcp_handle_ack(netif);
+#if DHCP_DOES_ARP_CHECK
+ /* check if the acknowledged lease address is already in use */
+ dhcp_check(netif);
+#else
+ /* bind interface to the acknowledged lease address */
+ dhcp_bind(netif);
+#endif
+ }
+ /* already bound to the given lease address? */
+ else if ((dhcp->state == DHCP_REBOOTING) || (dhcp->state == DHCP_REBINDING) || (dhcp->state == DHCP_RENEWING)) {
+ dhcp_handle_ack(netif);
+ dhcp_bind(netif);
+ }
+ }
+ /* received a DHCP_NAK in appropriate state? */
+ else if ((msg_type == DHCP_NAK) &&
+ ((dhcp->state == DHCP_REBOOTING) || (dhcp->state == DHCP_REQUESTING) ||
+ (dhcp->state == DHCP_REBINDING) || (dhcp->state == DHCP_RENEWING ))) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("DHCP_NAK received\n"));
+ dhcp_handle_nak(netif);
+ }
+ /* received a DHCP_OFFER in DHCP_SELECTING state? */
+ else if ((msg_type == DHCP_OFFER) && (dhcp->state == DHCP_SELECTING)) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("DHCP_OFFER received in DHCP_SELECTING state\n"));
+ dhcp->request_timeout = 0;
+ /* remember offered lease */
+ dhcp_handle_offer(netif);
+ }
+free_pbuf_and_return:
+ dhcp->msg_in = NULL;
+ pbuf_free(p);
+}
+
+/**
+ * Create a DHCP request, fill in common headers
+ *
+ * @param netif the netif under DHCP control
+ * @param dhcp dhcp control struct
+ * @param message_type message type of the request
+ */
+static err_t
+dhcp_create_msg(struct netif *netif, struct dhcp *dhcp, u8_t message_type)
+{
+ u16_t i;
+#ifndef DHCP_GLOBAL_XID
+ /** default global transaction identifier starting value (easy to match
+ * with a packet analyser). We simply increment for each new request.
+ * Predefine DHCP_GLOBAL_XID to a better value or a function call to generate one
+ * at runtime, any supporting function prototypes can be defined in DHCP_GLOBAL_XID_HEADER */
+#if DHCP_CREATE_RAND_XID && defined(LWIP_RAND)
+ static u32_t xid;
+#else /* DHCP_CREATE_RAND_XID && defined(LWIP_RAND) */
+ static u32_t xid = 0xABCD0000;
+#endif /* DHCP_CREATE_RAND_XID && defined(LWIP_RAND) */
+#else
+ if (!xid_initialised) {
+ xid = DHCP_GLOBAL_XID;
+ xid_initialised = !xid_initialised;
+ }
+#endif
+ LWIP_ERROR("dhcp_create_msg: netif != NULL", (netif != NULL), return ERR_ARG;);
+ LWIP_ERROR("dhcp_create_msg: dhcp != NULL", (dhcp != NULL), return ERR_VAL;);
+ LWIP_ASSERT("dhcp_create_msg: dhcp->p_out == NULL", dhcp->p_out == NULL);
+ LWIP_ASSERT("dhcp_create_msg: dhcp->msg_out == NULL", dhcp->msg_out == NULL);
+ dhcp->p_out = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct dhcp_msg), PBUF_RAM);
+ if (dhcp->p_out == NULL) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
+ ("dhcp_create_msg(): could not allocate pbuf\n"));
+ return ERR_MEM;
+ }
+ LWIP_ASSERT("dhcp_create_msg: check that first pbuf can hold struct dhcp_msg",
+ (dhcp->p_out->len >= sizeof(struct dhcp_msg)));
+
+ /* DHCP_REQUEST should reuse 'xid' from DHCPOFFER */
+ if (message_type != DHCP_REQUEST) {
+ /* reuse transaction identifier in retransmissions */
+ if (dhcp->tries == 0) {
+#if DHCP_CREATE_RAND_XID && defined(LWIP_RAND)
+ xid = LWIP_RAND();
+#else /* DHCP_CREATE_RAND_XID && defined(LWIP_RAND) */
+ xid++;
+#endif /* DHCP_CREATE_RAND_XID && defined(LWIP_RAND) */
+ }
+ dhcp->xid = xid;
+ }
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE,
+ ("transaction id xid(%"X32_F")\n", xid));
+
+ dhcp->msg_out = (struct dhcp_msg *)dhcp->p_out->payload;
+
+ dhcp->msg_out->op = DHCP_BOOTREQUEST;
+ /* TODO: make link layer independent */
+ dhcp->msg_out->htype = DHCP_HTYPE_ETH;
+ dhcp->msg_out->hlen = netif->hwaddr_len;
+ dhcp->msg_out->hops = 0;
+ dhcp->msg_out->xid = htonl(dhcp->xid);
+ dhcp->msg_out->secs = 0;
+ /* we don't need the broadcast flag since we can receive unicast traffic
+ before being fully configured! */
+ dhcp->msg_out->flags = 0;
+ ip_addr_set_zero(&dhcp->msg_out->ciaddr);
+ /* set ciaddr to netif->ip_addr based on message_type and state */
+ if ((message_type == DHCP_INFORM) || (message_type == DHCP_DECLINE) || (message_type == DHCP_RELEASE) ||
+ ((message_type == DHCP_REQUEST) && /* DHCP_BOUND not used for sending! */
+ ((dhcp->state==DHCP_RENEWING) || dhcp->state==DHCP_REBINDING))) {
+ ip_addr_copy(dhcp->msg_out->ciaddr, netif->ip_addr);
+ }
+ ip_addr_set_zero(&dhcp->msg_out->yiaddr);
+ ip_addr_set_zero(&dhcp->msg_out->siaddr);
+ ip_addr_set_zero(&dhcp->msg_out->giaddr);
+ for (i = 0; i < DHCP_CHADDR_LEN; i++) {
+ /* copy netif hardware address, pad with zeroes */
+ dhcp->msg_out->chaddr[i] = (i < netif->hwaddr_len && i < NETIF_MAX_HWADDR_LEN) ? netif->hwaddr[i] : 0/* pad byte*/;
+ }
+ for (i = 0; i < DHCP_SNAME_LEN; i++) {
+ dhcp->msg_out->sname[i] = 0;
+ }
+ for (i = 0; i < DHCP_FILE_LEN; i++) {
+ dhcp->msg_out->file[i] = 0;
+ }
+ dhcp->msg_out->cookie = PP_HTONL(DHCP_MAGIC_COOKIE);
+ dhcp->options_out_len = 0;
+ /* fill options field with an incrementing array (for debugging purposes) */
+ for (i = 0; i < DHCP_OPTIONS_LEN; i++) {
+ dhcp->msg_out->options[i] = (u8_t)i; /* for debugging only, no matter if truncated */
+ }
+ /* Add option MESSAGE_TYPE */
+ dhcp_option(dhcp, DHCP_OPTION_MESSAGE_TYPE, DHCP_OPTION_MESSAGE_TYPE_LEN);
+ dhcp_option_byte(dhcp, message_type);
+ return ERR_OK;
+}
+
+/**
+ * Free previously allocated memory used to send a DHCP request.
+ *
+ * @param dhcp the dhcp struct to free the request from
+ */
+static void
+dhcp_delete_msg(struct dhcp *dhcp)
+{
+ LWIP_ERROR("dhcp_delete_msg: dhcp != NULL", (dhcp != NULL), return;);
+ LWIP_ASSERT("dhcp_delete_msg: dhcp->p_out != NULL", dhcp->p_out != NULL);
+ LWIP_ASSERT("dhcp_delete_msg: dhcp->msg_out != NULL", dhcp->msg_out != NULL);
+ if (dhcp->p_out != NULL) {
+ pbuf_free(dhcp->p_out);
+ }
+ dhcp->p_out = NULL;
+ dhcp->msg_out = NULL;
+}
+
+/**
+ * Add a DHCP message trailer
+ *
+ * Adds the END option to the DHCP message, and if
+ * necessary, up to three padding bytes.
+ *
+ * @param dhcp DHCP state structure
+ */
+static void
+dhcp_option_trailer(struct dhcp *dhcp)
+{
+ LWIP_ERROR("dhcp_option_trailer: dhcp != NULL", (dhcp != NULL), return;);
+ LWIP_ASSERT("dhcp_option_trailer: dhcp->msg_out != NULL\n", dhcp->msg_out != NULL);
+ LWIP_ASSERT("dhcp_option_trailer: dhcp->options_out_len < DHCP_OPTIONS_LEN\n", dhcp->options_out_len < DHCP_OPTIONS_LEN);
+ dhcp->msg_out->options[dhcp->options_out_len++] = DHCP_OPTION_END;
+ /* packet is too small, or not 4 byte aligned? */
+ while (((dhcp->options_out_len < DHCP_MIN_OPTIONS_LEN) || (dhcp->options_out_len & 3)) &&
+ (dhcp->options_out_len < DHCP_OPTIONS_LEN)) {
+ /* add a fill/padding byte */
+ dhcp->msg_out->options[dhcp->options_out_len++] = 0;
+ }
+}
+
+#endif /* LWIP_DHCP */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/dns.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/dns.c
new file mode 100644
index 0000000..12c6f16
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/dns.c
@@ -0,0 +1,1573 @@
+/**
+ * @file
+ * DNS - host name to IP address resolver.
+ *
+ * @defgroup dns DNS
+ * @ingroup callbackstyle_api
+ *
+ * Implements a DNS host name to IP address resolver.
+ *
+ * The lwIP DNS resolver functions are used to lookup a host name and
+ * map it to a numerical IP address. It maintains a list of resolved
+ * hostnames that can be queried with the dns_lookup() function.
+ * New hostnames can be resolved using the dns_query() function.
+ *
+ * The lwIP version of the resolver also adds a non-blocking version of
+ * gethostbyname() that will work with a raw API application. This function
+ * checks for an IP address string first and converts it if it is valid.
+ * gethostbyname() then does a dns_lookup() to see if the name is
+ * already in the table. If so, the IP is returned. If not, a query is
+ * issued and the function returns with a ERR_INPROGRESS status. The app
+ * using the dns client must then go into a waiting state.
+ *
+ * Once a hostname has been resolved (or found to be non-existent),
+ * the resolver code calls a specified callback function (which
+ * must be implemented by the module that uses the resolver).
+ *
+ * Multicast DNS queries are supported for names ending on ".local".
+ * However, only "One-Shot Multicast DNS Queries" are supported (RFC 6762
+ * chapter 5.1), this is not a fully compliant implementation of continuous
+ * mDNS querying!
+ *
+ * All functions must be called from TCPIP thread.
+ *
+ * @see @ref netconn_common for thread-safe access.
+ */
+
+/*
+ * Port to lwIP from uIP
+ * by Jim Pettinato April 2007
+ *
+ * security fixes and more by Simon Goldschmidt
+ *
+ * uIP version Copyright (c) 2002-2003, Adam Dunkels.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*-----------------------------------------------------------------------------
+ * RFC 1035 - Domain names - implementation and specification
+ * RFC 2181 - Clarifications to the DNS Specification
+ *----------------------------------------------------------------------------*/
+
+/** @todo: define good default values (rfc compliance) */
+/** @todo: improve answer parsing, more checkings... */
+/** @todo: check RFC1035 - 7.3. Processing responses */
+/** @todo: one-shot mDNS: dual-stack fallback to another IP version */
+
+/*-----------------------------------------------------------------------------
+ * Includes
+ *----------------------------------------------------------------------------*/
+
+#include "lwip/opt.h"
+
+#if LWIP_DNS /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/def.h"
+#include "lwip/udp.h"
+#include "lwip/mem.h"
+#include "lwip/memp.h"
+#include "lwip/dns.h"
+#include "lwip/prot/dns.h"
+
+#include <string.h>
+
+/** Random generator function to create random TXIDs and source ports for queries */
+#ifndef DNS_RAND_TXID
+#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_XID) != 0)
+#define DNS_RAND_TXID LWIP_RAND
+#else
+static u16_t dns_txid;
+#define DNS_RAND_TXID() (++dns_txid)
+#endif
+#endif
+
+/** Limits the source port to be >= 1024 by default */
+#ifndef DNS_PORT_ALLOWED
+#define DNS_PORT_ALLOWED(port) ((port) >= 1024)
+#endif
+
+/** DNS maximum number of retries when asking for a name, before "timeout". */
+#ifndef DNS_MAX_RETRIES
+#define DNS_MAX_RETRIES 4
+#endif
+
+/** DNS resource record max. TTL (one week as default) */
+#ifndef DNS_MAX_TTL
+#define DNS_MAX_TTL 604800
+#elif DNS_MAX_TTL > 0x7FFFFFFF
+#error DNS_MAX_TTL must be a positive 32-bit value
+#endif
+
+#if DNS_TABLE_SIZE > 255
+#error DNS_TABLE_SIZE must fit into an u8_t
+#endif
+#if DNS_MAX_SERVERS > 255
+#error DNS_MAX_SERVERS must fit into an u8_t
+#endif
+
+/* The number of parallel requests (i.e. calls to dns_gethostbyname
+ * that cannot be answered from the DNS table.
+ * This is set to the table size by default.
+ */
+#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING) != 0)
+#ifndef DNS_MAX_REQUESTS
+#define DNS_MAX_REQUESTS DNS_TABLE_SIZE
+#else
+#if DNS_MAX_REQUESTS > 255
+#error DNS_MAX_REQUESTS must fit into an u8_t
+#endif
+#endif
+#else
+/* In this configuration, both arrays have to have the same size and are used
+ * like one entry (used/free) */
+#define DNS_MAX_REQUESTS DNS_TABLE_SIZE
+#endif
+
+/* The number of UDP source ports used in parallel */
+#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0)
+#ifndef DNS_MAX_SOURCE_PORTS
+#define DNS_MAX_SOURCE_PORTS DNS_MAX_REQUESTS
+#else
+#if DNS_MAX_SOURCE_PORTS > 255
+#error DNS_MAX_SOURCE_PORTS must fit into an u8_t
+#endif
+#endif
+#else
+#ifdef DNS_MAX_SOURCE_PORTS
+#undef DNS_MAX_SOURCE_PORTS
+#endif
+#define DNS_MAX_SOURCE_PORTS 1
+#endif
+
+#if LWIP_IPV4 && LWIP_IPV6
+#define LWIP_DNS_ADDRTYPE_IS_IPV6(t) (((t) == LWIP_DNS_ADDRTYPE_IPV6_IPV4) || ((t) == LWIP_DNS_ADDRTYPE_IPV6))
+#define LWIP_DNS_ADDRTYPE_MATCH_IP(t, ip) (IP_IS_V6_VAL(ip) ? LWIP_DNS_ADDRTYPE_IS_IPV6(t) : (!LWIP_DNS_ADDRTYPE_IS_IPV6(t)))
+#define LWIP_DNS_ADDRTYPE_ARG(x) , x
+#define LWIP_DNS_ADDRTYPE_ARG_OR_ZERO(x) x
+#define LWIP_DNS_SET_ADDRTYPE(x, y) do { x = y; } while(0)
+#else
+#if LWIP_IPV6
+#define LWIP_DNS_ADDRTYPE_IS_IPV6(t) 1
+#else
+#define LWIP_DNS_ADDRTYPE_IS_IPV6(t) 0
+#endif
+#define LWIP_DNS_ADDRTYPE_MATCH_IP(t, ip) 1
+#define LWIP_DNS_ADDRTYPE_ARG(x)
+#define LWIP_DNS_ADDRTYPE_ARG_OR_ZERO(x) 0
+#define LWIP_DNS_SET_ADDRTYPE(x, y)
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+
+#if LWIP_DNS_SUPPORT_MDNS_QUERIES
+#define LWIP_DNS_ISMDNS_ARG(x) , x
+#else
+#define LWIP_DNS_ISMDNS_ARG(x)
+#endif
+
+/** DNS query message structure.
+ No packing needed: only used locally on the stack. */
+struct dns_query {
+ /* DNS query record starts with either a domain name or a pointer
+ to a name already present somewhere in the packet. */
+ u16_t type;
+ u16_t cls;
+};
+#define SIZEOF_DNS_QUERY 4
+
+/** DNS answer message structure.
+ No packing needed: only used locally on the stack. */
+struct dns_answer {
+ /* DNS answer record starts with either a domain name or a pointer
+ to a name already present somewhere in the packet. */
+ u16_t type;
+ u16_t cls;
+ u32_t ttl;
+ u16_t len;
+};
+#define SIZEOF_DNS_ANSWER 10
+/* maximum allowed size for the struct due to non-packed */
+#define SIZEOF_DNS_ANSWER_ASSERT 12
+
+/* DNS table entry states */
+typedef enum {
+ DNS_STATE_UNUSED = 0,
+ DNS_STATE_NEW = 1,
+ DNS_STATE_ASKING = 2,
+ DNS_STATE_DONE = 3
+} dns_state_enum_t;
+
+/** DNS table entry */
+struct dns_table_entry {
+ u32_t ttl;
+ ip_addr_t ipaddr;
+ u16_t txid;
+ u8_t state;
+ u8_t server_idx;
+ u8_t tmr;
+ u8_t retries;
+ u8_t seqno;
+#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0)
+ u8_t pcb_idx;
+#endif
+ char name[DNS_MAX_NAME_LENGTH];
+#if LWIP_IPV4 && LWIP_IPV6
+ u8_t reqaddrtype;
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+#if LWIP_DNS_SUPPORT_MDNS_QUERIES
+ u8_t is_mdns;
+#endif
+};
+
+/** DNS request table entry: used when dns_gehostbyname cannot answer the
+ * request from the DNS table */
+struct dns_req_entry {
+ /* pointer to callback on DNS query done */
+ dns_found_callback found;
+ /* argument passed to the callback function */
+ void *arg;
+#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING) != 0)
+ u8_t dns_table_idx;
+#endif
+#if LWIP_IPV4 && LWIP_IPV6
+ u8_t reqaddrtype;
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+};
+
+#if DNS_LOCAL_HOSTLIST
+
+#if DNS_LOCAL_HOSTLIST_IS_DYNAMIC
+/** Local host-list. For hostnames in this list, no
+ * external name resolution is performed */
+static struct local_hostlist_entry *local_hostlist_dynamic;
+#else /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
+
+/** Defining this allows the local_hostlist_static to be placed in a different
+ * linker section (e.g. FLASH) */
+#ifndef DNS_LOCAL_HOSTLIST_STORAGE_PRE
+#define DNS_LOCAL_HOSTLIST_STORAGE_PRE static
+#endif /* DNS_LOCAL_HOSTLIST_STORAGE_PRE */
+/** Defining this allows the local_hostlist_static to be placed in a different
+ * linker section (e.g. FLASH) */
+#ifndef DNS_LOCAL_HOSTLIST_STORAGE_POST
+#define DNS_LOCAL_HOSTLIST_STORAGE_POST
+#endif /* DNS_LOCAL_HOSTLIST_STORAGE_POST */
+DNS_LOCAL_HOSTLIST_STORAGE_PRE struct local_hostlist_entry local_hostlist_static[]
+ DNS_LOCAL_HOSTLIST_STORAGE_POST = DNS_LOCAL_HOSTLIST_INIT;
+
+#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
+
+static void dns_init_local(void);
+static err_t dns_lookup_local(const char *hostname, ip_addr_t *addr LWIP_DNS_ADDRTYPE_ARG(u8_t dns_addrtype));
+#endif /* DNS_LOCAL_HOSTLIST */
+
+
+/* forward declarations */
+static void dns_recv(void *s, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port);
+static void dns_check_entries(void);
+static void dns_call_found(u8_t idx, ip_addr_t* addr);
+
+/*-----------------------------------------------------------------------------
+ * Globals
+ *----------------------------------------------------------------------------*/
+
+/* DNS variables */
+static struct udp_pcb *dns_pcbs[DNS_MAX_SOURCE_PORTS];
+#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0)
+static u8_t dns_last_pcb_idx;
+#endif
+static u8_t dns_seqno;
+static struct dns_table_entry dns_table[DNS_TABLE_SIZE];
+static struct dns_req_entry dns_requests[DNS_MAX_REQUESTS];
+static ip_addr_t dns_servers[DNS_MAX_SERVERS];
+
+#if LWIP_IPV4
+const ip_addr_t dns_mquery_v4group = DNS_MQUERY_IPV4_GROUP_INIT;
+#endif /* LWIP_IPV4 */
+#if LWIP_IPV6
+const ip_addr_t dns_mquery_v6group = DNS_MQUERY_IPV6_GROUP_INIT;
+#endif /* LWIP_IPV6 */
+
+/**
+ * Initialize the resolver: set up the UDP pcb and configure the default server
+ * (if DNS_SERVER_ADDRESS is set).
+ */
+void
+dns_init(void)
+{
+#ifdef DNS_SERVER_ADDRESS
+ /* initialize default DNS server address */
+ ip_addr_t dnsserver;
+ DNS_SERVER_ADDRESS(&dnsserver);
+ dns_setserver(0, &dnsserver);
+#endif /* DNS_SERVER_ADDRESS */
+
+ LWIP_ASSERT("sanity check SIZEOF_DNS_QUERY",
+ sizeof(struct dns_query) == SIZEOF_DNS_QUERY);
+ LWIP_ASSERT("sanity check SIZEOF_DNS_ANSWER",
+ sizeof(struct dns_answer) <= SIZEOF_DNS_ANSWER_ASSERT);
+
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_init: initializing\n"));
+
+ /* if dns client not yet initialized... */
+#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) == 0)
+ if (dns_pcbs[0] == NULL) {
+ dns_pcbs[0] = udp_new_ip_type(IPADDR_TYPE_ANY);
+ LWIP_ASSERT("dns_pcbs[0] != NULL", dns_pcbs[0] != NULL);
+
+ /* initialize DNS table not needed (initialized to zero since it is a
+ * global variable) */
+ LWIP_ASSERT("For implicit initialization to work, DNS_STATE_UNUSED needs to be 0",
+ DNS_STATE_UNUSED == 0);
+
+ /* initialize DNS client */
+ udp_bind(dns_pcbs[0], IP_ANY_TYPE, 0);
+ udp_recv(dns_pcbs[0], dns_recv, NULL);
+ }
+#endif
+
+#if DNS_LOCAL_HOSTLIST
+ dns_init_local();
+#endif
+}
+
+/**
+ * @ingroup dns
+ * Initialize one of the DNS servers.
+ *
+ * @param numdns the index of the DNS server to set must be < DNS_MAX_SERVERS
+ * @param dnsserver IP address of the DNS server to set
+ */
+void
+dns_setserver(u8_t numdns, const ip_addr_t *dnsserver)
+{
+ if (numdns < DNS_MAX_SERVERS) {
+ if (dnsserver != NULL) {
+ dns_servers[numdns] = (*dnsserver);
+ } else {
+ dns_servers[numdns] = *IP_ADDR_ANY;
+ }
+ }
+}
+
+/**
+ * @ingroup dns
+ * Obtain one of the currently configured DNS server.
+ *
+ * @param numdns the index of the DNS server
+ * @return IP address of the indexed DNS server or "ip_addr_any" if the DNS
+ * server has not been configured.
+ */
+const ip_addr_t*
+dns_getserver(u8_t numdns)
+{
+ if (numdns < DNS_MAX_SERVERS) {
+ return &dns_servers[numdns];
+ } else {
+ return IP_ADDR_ANY;
+ }
+}
+
+/**
+ * The DNS resolver client timer - handle retries and timeouts and should
+ * be called every DNS_TMR_INTERVAL milliseconds (every second by default).
+ */
+void
+dns_tmr(void)
+{
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_tmr: dns_check_entries\n"));
+ dns_check_entries();
+}
+
+#if DNS_LOCAL_HOSTLIST
+static void
+dns_init_local(void)
+{
+#if DNS_LOCAL_HOSTLIST_IS_DYNAMIC && defined(DNS_LOCAL_HOSTLIST_INIT)
+ size_t i;
+ struct local_hostlist_entry *entry;
+ /* Dynamic: copy entries from DNS_LOCAL_HOSTLIST_INIT to list */
+ struct local_hostlist_entry local_hostlist_init[] = DNS_LOCAL_HOSTLIST_INIT;
+ size_t namelen;
+ for (i = 0; i < LWIP_ARRAYSIZE(local_hostlist_init); i++) {
+ struct local_hostlist_entry *init_entry = &local_hostlist_init[i];
+ LWIP_ASSERT("invalid host name (NULL)", init_entry->name != NULL);
+ namelen = strlen(init_entry->name);
+ LWIP_ASSERT("namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN", namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN);
+ entry = (struct local_hostlist_entry *)memp_malloc(MEMP_LOCALHOSTLIST);
+ LWIP_ASSERT("mem-error in dns_init_local", entry != NULL);
+ if (entry != NULL) {
+ char* entry_name = (char*)entry + sizeof(struct local_hostlist_entry);
+ MEMCPY(entry_name, init_entry->name, namelen);
+ entry_name[namelen] = 0;
+ entry->name = entry_name;
+ entry->addr = init_entry->addr;
+ entry->next = local_hostlist_dynamic;
+ local_hostlist_dynamic = entry;
+ }
+ }
+#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC && defined(DNS_LOCAL_HOSTLIST_INIT) */
+}
+
+/**
+ * @ingroup dns
+ * Iterate the local host-list for a hostname.
+ *
+ * @param iterator_fn a function that is called for every entry in the local host-list
+ * @param iterator_arg 3rd argument passed to iterator_fn
+ * @return the number of entries in the local host-list
+ */
+size_t
+dns_local_iterate(dns_found_callback iterator_fn, void *iterator_arg)
+{
+ size_t i;
+#if DNS_LOCAL_HOSTLIST_IS_DYNAMIC
+ struct local_hostlist_entry *entry = local_hostlist_dynamic;
+ i = 0;
+ while (entry != NULL) {
+ if (iterator_fn != NULL) {
+ iterator_fn(entry->name, &entry->addr, iterator_arg);
+ }
+ i++;
+ entry = entry->next;
+ }
+#else /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
+ for (i = 0; i < LWIP_ARRAYSIZE(local_hostlist_static); i++) {
+ if (iterator_fn != NULL) {
+ iterator_fn(local_hostlist_static[i].name, &local_hostlist_static[i].addr, iterator_arg);
+ }
+ }
+#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
+ return i;
+}
+
+/**
+ * @ingroup dns
+ * Scans the local host-list for a hostname.
+ *
+ * @param hostname Hostname to look for in the local host-list
+ * @param addr the first IP address for the hostname in the local host-list or
+ * IPADDR_NONE if not found.
+ * @param dns_addrtype - LWIP_DNS_ADDRTYPE_IPV4_IPV6: try to resolve IPv4 (ATTENTION: no fallback here!)
+ * - LWIP_DNS_ADDRTYPE_IPV6_IPV4: try to resolve IPv6 (ATTENTION: no fallback here!)
+ * - LWIP_DNS_ADDRTYPE_IPV4: try to resolve IPv4 only
+ * - LWIP_DNS_ADDRTYPE_IPV6: try to resolve IPv6 only
+ * @return ERR_OK if found, ERR_ARG if not found
+ */
+err_t
+dns_local_lookup(const char *hostname, ip_addr_t *addr, u8_t dns_addrtype)
+{
+ LWIP_UNUSED_ARG(dns_addrtype);
+ return dns_lookup_local(hostname, addr LWIP_DNS_ADDRTYPE_ARG(dns_addrtype));
+}
+
+/* Internal implementation for dns_local_lookup and dns_lookup */
+static err_t
+dns_lookup_local(const char *hostname, ip_addr_t *addr LWIP_DNS_ADDRTYPE_ARG(u8_t dns_addrtype))
+{
+#if DNS_LOCAL_HOSTLIST_IS_DYNAMIC
+ struct local_hostlist_entry *entry = local_hostlist_dynamic;
+ while (entry != NULL) {
+ if ((lwip_stricmp(entry->name, hostname) == 0) &&
+ LWIP_DNS_ADDRTYPE_MATCH_IP(dns_addrtype, entry->addr)) {
+ if (addr) {
+ ip_addr_copy(*addr, entry->addr);
+ }
+ return ERR_OK;
+ }
+ entry = entry->next;
+ }
+#else /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
+ size_t i;
+ for (i = 0; i < LWIP_ARRAYSIZE(local_hostlist_static); i++) {
+ if ((lwip_stricmp(local_hostlist_static[i].name, hostname) == 0) &&
+ LWIP_DNS_ADDRTYPE_MATCH_IP(dns_addrtype, local_hostlist_static[i].addr)) {
+ if (addr) {
+ ip_addr_copy(*addr, local_hostlist_static[i].addr);
+ }
+ return ERR_OK;
+ }
+ }
+#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
+ return ERR_ARG;
+}
+
+#if DNS_LOCAL_HOSTLIST_IS_DYNAMIC
+/**
+ * @ingroup dns
+ * Remove all entries from the local host-list for a specific hostname
+ * and/or IP address
+ *
+ * @param hostname hostname for which entries shall be removed from the local
+ * host-list
+ * @param addr address for which entries shall be removed from the local host-list
+ * @return the number of removed entries
+ */
+int
+dns_local_removehost(const char *hostname, const ip_addr_t *addr)
+{
+ int removed = 0;
+ struct local_hostlist_entry *entry = local_hostlist_dynamic;
+ struct local_hostlist_entry *last_entry = NULL;
+ while (entry != NULL) {
+ if (((hostname == NULL) || !lwip_stricmp(entry->name, hostname)) &&
+ ((addr == NULL) || ip_addr_cmp(&entry->addr, addr))) {
+ struct local_hostlist_entry *free_entry;
+ if (last_entry != NULL) {
+ last_entry->next = entry->next;
+ } else {
+ local_hostlist_dynamic = entry->next;
+ }
+ free_entry = entry;
+ entry = entry->next;
+ memp_free(MEMP_LOCALHOSTLIST, free_entry);
+ removed++;
+ } else {
+ last_entry = entry;
+ entry = entry->next;
+ }
+ }
+ return removed;
+}
+
+/**
+ * @ingroup dns
+ * Add a hostname/IP address pair to the local host-list.
+ * Duplicates are not checked.
+ *
+ * @param hostname hostname of the new entry
+ * @param addr IP address of the new entry
+ * @return ERR_OK if succeeded or ERR_MEM on memory error
+ */
+err_t
+dns_local_addhost(const char *hostname, const ip_addr_t *addr)
+{
+ struct local_hostlist_entry *entry;
+ size_t namelen;
+ char* entry_name;
+ LWIP_ASSERT("invalid host name (NULL)", hostname != NULL);
+ namelen = strlen(hostname);
+ LWIP_ASSERT("namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN", namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN);
+ entry = (struct local_hostlist_entry *)memp_malloc(MEMP_LOCALHOSTLIST);
+ if (entry == NULL) {
+ return ERR_MEM;
+ }
+ entry_name = (char*)entry + sizeof(struct local_hostlist_entry);
+ MEMCPY(entry_name, hostname, namelen);
+ entry_name[namelen] = 0;
+ entry->name = entry_name;
+ ip_addr_copy(entry->addr, *addr);
+ entry->next = local_hostlist_dynamic;
+ local_hostlist_dynamic = entry;
+ return ERR_OK;
+}
+#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC*/
+#endif /* DNS_LOCAL_HOSTLIST */
+
+/**
+ * @ingroup dns
+ * Look up a hostname in the array of known hostnames.
+ *
+ * @note This function only looks in the internal array of known
+ * hostnames, it does not send out a query for the hostname if none
+ * was found. The function dns_enqueue() can be used to send a query
+ * for a hostname.
+ *
+ * @param name the hostname to look up
+ * @param addr the hostname's IP address, as u32_t (instead of ip_addr_t to
+ * better check for failure: != IPADDR_NONE) or IPADDR_NONE if the hostname
+ * was not found in the cached dns_table.
+ * @return ERR_OK if found, ERR_ARG if not found
+ */
+static err_t
+dns_lookup(const char *name, ip_addr_t *addr LWIP_DNS_ADDRTYPE_ARG(u8_t dns_addrtype))
+{
+ u8_t i;
+#if DNS_LOCAL_HOSTLIST || defined(DNS_LOOKUP_LOCAL_EXTERN)
+#endif /* DNS_LOCAL_HOSTLIST || defined(DNS_LOOKUP_LOCAL_EXTERN) */
+#if DNS_LOCAL_HOSTLIST
+ if (dns_lookup_local(name, addr LWIP_DNS_ADDRTYPE_ARG(dns_addrtype)) == ERR_OK) {
+ return ERR_OK;
+ }
+#endif /* DNS_LOCAL_HOSTLIST */
+#ifdef DNS_LOOKUP_LOCAL_EXTERN
+ if (DNS_LOOKUP_LOCAL_EXTERN(name, addr, LWIP_DNS_ADDRTYPE_ARG_OR_ZERO(dns_addrtype)) == ERR_OK) {
+ return ERR_OK;
+ }
+#endif /* DNS_LOOKUP_LOCAL_EXTERN */
+
+ /* Walk through name list, return entry if found. If not, return NULL. */
+ for (i = 0; i < DNS_TABLE_SIZE; ++i) {
+ if ((dns_table[i].state == DNS_STATE_DONE) &&
+ (lwip_strnicmp(name, dns_table[i].name, sizeof(dns_table[i].name)) == 0) &&
+ LWIP_DNS_ADDRTYPE_MATCH_IP(dns_addrtype, dns_table[i].ipaddr)) {
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_lookup: \"%s\": found = ", name));
+ ip_addr_debug_print(DNS_DEBUG, &(dns_table[i].ipaddr));
+ LWIP_DEBUGF(DNS_DEBUG, ("\n"));
+ if (addr) {
+ ip_addr_copy(*addr, dns_table[i].ipaddr);
+ }
+ return ERR_OK;
+ }
+ }
+
+ return ERR_ARG;
+}
+
+/**
+ * Compare the "dotted" name "query" with the encoded name "response"
+ * to make sure an answer from the DNS server matches the current dns_table
+ * entry (otherwise, answers might arrive late for hostname not on the list
+ * any more).
+ *
+ * @param query hostname (not encoded) from the dns_table
+ * @param p pbuf containing the encoded hostname in the DNS response
+ * @param start_offset offset into p where the name starts
+ * @return 0xFFFF: names differ, other: names equal -> offset behind name
+ */
+static u16_t
+dns_compare_name(const char *query, struct pbuf* p, u16_t start_offset)
+{
+ int n;
+ u16_t response_offset = start_offset;
+
+ do {
+ n = pbuf_try_get_at(p, response_offset++);
+ if (n < 0) {
+ return 0xFFFF;
+ }
+ /** @see RFC 1035 - 4.1.4. Message compression */
+ if ((n & 0xc0) == 0xc0) {
+ /* Compressed name: cannot be equal since we don't send them */
+ return 0xFFFF;
+ } else {
+ /* Not compressed name */
+ while (n > 0) {
+ int c = pbuf_try_get_at(p, response_offset);
+ if (c < 0) {
+ return 0xFFFF;
+ }
+ if ((*query) != (u8_t)c) {
+ return 0xFFFF;
+ }
+ ++response_offset;
+ ++query;
+ --n;
+ }
+ ++query;
+ }
+ n = pbuf_try_get_at(p, response_offset);
+ if (n < 0) {
+ return 0xFFFF;
+ }
+ } while (n != 0);
+
+ return response_offset + 1;
+}
+
+/**
+ * Walk through a compact encoded DNS name and return the end of the name.
+ *
+ * @param p pbuf containing the name
+ * @param query_idx start index into p pointing to encoded DNS name in the DNS server response
+ * @return index to end of the name
+ */
+static u16_t
+dns_skip_name(struct pbuf* p, u16_t query_idx)
+{
+ int n;
+ u16_t offset = query_idx;
+
+ do {
+ n = pbuf_try_get_at(p, offset++);
+ if (n < 0) {
+ return 0xFFFF;
+ }
+ /** @see RFC 1035 - 4.1.4. Message compression */
+ if ((n & 0xc0) == 0xc0) {
+ /* Compressed name: since we only want to skip it (not check it), stop here */
+ break;
+ } else {
+ /* Not compressed name */
+ if (offset + n >= p->tot_len) {
+ return 0xFFFF;
+ }
+ offset = (u16_t)(offset + n);
+ }
+ n = pbuf_try_get_at(p, offset);
+ if (n < 0) {
+ return 0xFFFF;
+ }
+ } while (n != 0);
+
+ return offset + 1;
+}
+
+/**
+ * Send a DNS query packet.
+ *
+ * @param idx the DNS table entry index for which to send a request
+ * @return ERR_OK if packet is sent; an err_t indicating the problem otherwise
+ */
+static err_t
+dns_send(u8_t idx)
+{
+ err_t err;
+ struct dns_hdr hdr;
+ struct dns_query qry;
+ struct pbuf *p;
+ u16_t query_idx, copy_len;
+ const char *hostname, *hostname_part;
+ u8_t n;
+ u8_t pcb_idx;
+ struct dns_table_entry* entry = &dns_table[idx];
+
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_send: dns_servers[%"U16_F"] \"%s\": request\n",
+ (u16_t)(entry->server_idx), entry->name));
+ LWIP_ASSERT("dns server out of array", entry->server_idx < DNS_MAX_SERVERS);
+ if (ip_addr_isany_val(dns_servers[entry->server_idx])
+#if LWIP_DNS_SUPPORT_MDNS_QUERIES
+ && !entry->is_mdns
+#endif
+ ) {
+ /* DNS server not valid anymore, e.g. PPP netif has been shut down */
+ /* call specified callback function if provided */
+ dns_call_found(idx, NULL);
+ /* flush this entry */
+ entry->state = DNS_STATE_UNUSED;
+ return ERR_OK;
+ }
+
+ /* if here, we have either a new query or a retry on a previous query to process */
+ p = pbuf_alloc(PBUF_TRANSPORT, (u16_t)(SIZEOF_DNS_HDR + strlen(entry->name) + 2 +
+ SIZEOF_DNS_QUERY), PBUF_RAM);
+ if (p != NULL) {
+ const ip_addr_t* dst;
+ u16_t dst_port;
+ /* fill dns header */
+ memset(&hdr, 0, SIZEOF_DNS_HDR);
+ hdr.id = lwip_htons(entry->txid);
+ hdr.flags1 = DNS_FLAG1_RD;
+ hdr.numquestions = PP_HTONS(1);
+ pbuf_take(p, &hdr, SIZEOF_DNS_HDR);
+ hostname = entry->name;
+ --hostname;
+
+ /* convert hostname into suitable query format. */
+ query_idx = SIZEOF_DNS_HDR;
+ do {
+ ++hostname;
+ hostname_part = hostname;
+ for (n = 0; *hostname != '.' && *hostname != 0; ++hostname) {
+ ++n;
+ }
+ copy_len = (u16_t)(hostname - hostname_part);
+ pbuf_put_at(p, query_idx, n);
+ pbuf_take_at(p, hostname_part, copy_len, query_idx + 1);
+ query_idx += n + 1;
+ } while (*hostname != 0);
+ pbuf_put_at(p, query_idx, 0);
+ query_idx++;
+
+ /* fill dns query */
+ if (LWIP_DNS_ADDRTYPE_IS_IPV6(entry->reqaddrtype)) {
+ qry.type = PP_HTONS(DNS_RRTYPE_AAAA);
+ } else {
+ qry.type = PP_HTONS(DNS_RRTYPE_A);
+ }
+ qry.cls = PP_HTONS(DNS_RRCLASS_IN);
+ pbuf_take_at(p, &qry, SIZEOF_DNS_QUERY, query_idx);
+
+#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0)
+ pcb_idx = entry->pcb_idx;
+#else
+ pcb_idx = 0;
+#endif
+ /* send dns packet */
+ LWIP_DEBUGF(DNS_DEBUG, ("sending DNS request ID %d for name \"%s\" to server %d\r\n",
+ entry->txid, entry->name, entry->server_idx));
+#if LWIP_DNS_SUPPORT_MDNS_QUERIES
+ if (entry->is_mdns) {
+ dst_port = DNS_MQUERY_PORT;
+#if LWIP_IPV6
+ if (LWIP_DNS_ADDRTYPE_IS_IPV6(entry->reqaddrtype))
+ {
+ dst = &dns_mquery_v6group;
+ }
+#endif
+#if LWIP_IPV4 && LWIP_IPV6
+ else
+#endif
+#if LWIP_IPV4
+ {
+ dst = &dns_mquery_v4group;
+ }
+#endif
+ } else
+#endif /* LWIP_DNS_SUPPORT_MDNS_QUERIES */
+ {
+ dst_port = DNS_SERVER_PORT;
+ dst = &dns_servers[entry->server_idx];
+ }
+ err = udp_sendto(dns_pcbs[pcb_idx], p, dst, dst_port);
+
+ /* free pbuf */
+ pbuf_free(p);
+ } else {
+ err = ERR_MEM;
+ }
+
+ return err;
+}
+
+#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0)
+static struct udp_pcb*
+dns_alloc_random_port(void)
+{
+ err_t err;
+ struct udp_pcb* ret;
+
+ ret = udp_new_ip_type(IPADDR_TYPE_ANY);
+ if (ret == NULL) {
+ /* out of memory, have to reuse an existing pcb */
+ return NULL;
+ }
+ do {
+ u16_t port = (u16_t)DNS_RAND_TXID();
+ if (!DNS_PORT_ALLOWED(port)) {
+ /* this port is not allowed, try again */
+ err = ERR_USE;
+ continue;
+ }
+ err = udp_bind(ret, IP_ANY_TYPE, port);
+ } while (err == ERR_USE);
+ if (err != ERR_OK) {
+ udp_remove(ret);
+ return NULL;
+ }
+ udp_recv(ret, dns_recv, NULL);
+ return ret;
+}
+
+/**
+ * dns_alloc_pcb() - allocates a new pcb (or reuses an existing one) to be used
+ * for sending a request
+ *
+ * @return an index into dns_pcbs
+ */
+static u8_t
+dns_alloc_pcb(void)
+{
+ u8_t i;
+ u8_t idx;
+
+ for (i = 0; i < DNS_MAX_SOURCE_PORTS; i++) {
+ if (dns_pcbs[i] == NULL) {
+ break;
+ }
+ }
+ if (i < DNS_MAX_SOURCE_PORTS) {
+ dns_pcbs[i] = dns_alloc_random_port();
+ if (dns_pcbs[i] != NULL) {
+ /* succeeded */
+ dns_last_pcb_idx = i;
+ return i;
+ }
+ }
+ /* if we come here, creating a new UDP pcb failed, so we have to use
+ an already existing one */
+ for (i = 0, idx = dns_last_pcb_idx + 1; i < DNS_MAX_SOURCE_PORTS; i++, idx++) {
+ if (idx >= DNS_MAX_SOURCE_PORTS) {
+ idx = 0;
+ }
+ if (dns_pcbs[idx] != NULL) {
+ dns_last_pcb_idx = idx;
+ return idx;
+ }
+ }
+ return DNS_MAX_SOURCE_PORTS;
+}
+#endif /* ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0) */
+
+/**
+ * dns_call_found() - call the found callback and check if there are duplicate
+ * entries for the given hostname. If there are any, their found callback will
+ * be called and they will be removed.
+ *
+ * @param idx dns table index of the entry that is resolved or removed
+ * @param addr IP address for the hostname (or NULL on error or memory shortage)
+ */
+static void
+dns_call_found(u8_t idx, ip_addr_t* addr)
+{
+#if ((LWIP_DNS_SECURE & (LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING | LWIP_DNS_SECURE_RAND_SRC_PORT)) != 0)
+ u8_t i;
+#endif
+
+#if LWIP_IPV4 && LWIP_IPV6
+ if (addr != NULL) {
+ /* check that address type matches the request and adapt the table entry */
+ if (IP_IS_V6_VAL(*addr)) {
+ LWIP_ASSERT("invalid response", LWIP_DNS_ADDRTYPE_IS_IPV6(dns_table[idx].reqaddrtype));
+ dns_table[idx].reqaddrtype = LWIP_DNS_ADDRTYPE_IPV6;
+ } else {
+ LWIP_ASSERT("invalid response", !LWIP_DNS_ADDRTYPE_IS_IPV6(dns_table[idx].reqaddrtype));
+ dns_table[idx].reqaddrtype = LWIP_DNS_ADDRTYPE_IPV4;
+ }
+ }
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+
+#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING) != 0)
+ for (i = 0; i < DNS_MAX_REQUESTS; i++) {
+ if (dns_requests[i].found && (dns_requests[i].dns_table_idx == idx)) {
+ (*dns_requests[i].found)(dns_table[idx].name, addr, dns_requests[i].arg);
+ /* flush this entry */
+ dns_requests[i].found = NULL;
+ }
+ }
+#else
+ if (dns_requests[idx].found) {
+ (*dns_requests[idx].found)(dns_table[idx].name, addr, dns_requests[idx].arg);
+ }
+ dns_requests[idx].found = NULL;
+#endif
+#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0)
+ /* close the pcb used unless other request are using it */
+ for (i = 0; i < DNS_MAX_REQUESTS; i++) {
+ if (i == idx) {
+ continue; /* only check other requests */
+ }
+ if (dns_table[i].state == DNS_STATE_ASKING) {
+ if (dns_table[i].pcb_idx == dns_table[idx].pcb_idx) {
+ /* another request is still using the same pcb */
+ dns_table[idx].pcb_idx = DNS_MAX_SOURCE_PORTS;
+ break;
+ }
+ }
+ }
+ if (dns_table[idx].pcb_idx < DNS_MAX_SOURCE_PORTS) {
+ /* if we come here, the pcb is not used any more and can be removed */
+ udp_remove(dns_pcbs[dns_table[idx].pcb_idx]);
+ dns_pcbs[dns_table[idx].pcb_idx] = NULL;
+ dns_table[idx].pcb_idx = DNS_MAX_SOURCE_PORTS;
+ }
+#endif
+}
+
+/* Create a query transmission ID that is unique for all outstanding queries */
+static u16_t
+dns_create_txid(void)
+{
+ u16_t txid;
+ u8_t i;
+
+again:
+ txid = (u16_t)DNS_RAND_TXID();
+
+ /* check whether the ID is unique */
+ for (i = 0; i < DNS_TABLE_SIZE; i++) {
+ if ((dns_table[i].state == DNS_STATE_ASKING) &&
+ (dns_table[i].txid == txid)) {
+ /* ID already used by another pending query */
+ goto again;
+ }
+ }
+
+ return txid;
+}
+
+/**
+ * dns_check_entry() - see if entry has not yet been queried and, if so, sends out a query.
+ * Check an entry in the dns_table:
+ * - send out query for new entries
+ * - retry old pending entries on timeout (also with different servers)
+ * - remove completed entries from the table if their TTL has expired
+ *
+ * @param i index of the dns_table entry to check
+ */
+static void
+dns_check_entry(u8_t i)
+{
+ err_t err;
+ struct dns_table_entry *entry = &dns_table[i];
+
+ LWIP_ASSERT("array index out of bounds", i < DNS_TABLE_SIZE);
+
+ switch (entry->state) {
+ case DNS_STATE_NEW:
+ /* initialize new entry */
+ entry->txid = dns_create_txid();
+ entry->state = DNS_STATE_ASKING;
+ entry->server_idx = 0;
+ entry->tmr = 1;
+ entry->retries = 0;
+
+ /* send DNS packet for this entry */
+ err = dns_send(i);
+ if (err != ERR_OK) {
+ LWIP_DEBUGF(DNS_DEBUG | LWIP_DBG_LEVEL_WARNING,
+ ("dns_send returned error: %s\n", lwip_strerr(err)));
+ }
+ break;
+ case DNS_STATE_ASKING:
+ if (--entry->tmr == 0) {
+ if (++entry->retries == DNS_MAX_RETRIES) {
+ if ((entry->server_idx + 1 < DNS_MAX_SERVERS) && !ip_addr_isany_val(dns_servers[entry->server_idx + 1])
+#if LWIP_DNS_SUPPORT_MDNS_QUERIES
+ && !entry->is_mdns
+#endif /* LWIP_DNS_SUPPORT_MDNS_QUERIES */
+ ) {
+ /* change of server */
+ entry->server_idx++;
+ entry->tmr = 1;
+ entry->retries = 0;
+ } else {
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_check_entry: \"%s\": timeout\n", entry->name));
+ /* call specified callback function if provided */
+ dns_call_found(i, NULL);
+ /* flush this entry */
+ entry->state = DNS_STATE_UNUSED;
+ break;
+ }
+ } else {
+ /* wait longer for the next retry */
+ entry->tmr = entry->retries;
+ }
+
+ /* send DNS packet for this entry */
+ err = dns_send(i);
+ if (err != ERR_OK) {
+ LWIP_DEBUGF(DNS_DEBUG | LWIP_DBG_LEVEL_WARNING,
+ ("dns_send returned error: %s\n", lwip_strerr(err)));
+ }
+ }
+ break;
+ case DNS_STATE_DONE:
+ /* if the time to live is nul */
+ if ((entry->ttl == 0) || (--entry->ttl == 0)) {
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_check_entry: \"%s\": flush\n", entry->name));
+ /* flush this entry, there cannot be any related pending entries in this state */
+ entry->state = DNS_STATE_UNUSED;
+ }
+ break;
+ case DNS_STATE_UNUSED:
+ /* nothing to do */
+ break;
+ default:
+ LWIP_ASSERT("unknown dns_table entry state:", 0);
+ break;
+ }
+}
+
+/**
+ * Call dns_check_entry for each entry in dns_table - check all entries.
+ */
+static void
+dns_check_entries(void)
+{
+ u8_t i;
+
+ for (i = 0; i < DNS_TABLE_SIZE; ++i) {
+ dns_check_entry(i);
+ }
+}
+
+/**
+ * Save TTL and call dns_call_found for correct response.
+ */
+static void
+dns_correct_response(u8_t idx, u32_t ttl)
+{
+ struct dns_table_entry *entry = &dns_table[idx];
+
+ entry->state = DNS_STATE_DONE;
+
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response = ", entry->name));
+ ip_addr_debug_print(DNS_DEBUG, (&(entry->ipaddr)));
+ LWIP_DEBUGF(DNS_DEBUG, ("\n"));
+
+ /* read the answer resource record's TTL, and maximize it if needed */
+ entry->ttl = ttl;
+ if (entry->ttl > DNS_MAX_TTL) {
+ entry->ttl = DNS_MAX_TTL;
+ }
+ dns_call_found(idx, &entry->ipaddr);
+
+ if (entry->ttl == 0) {
+ /* RFC 883, page 29: "Zero values are
+ interpreted to mean that the RR can only be used for the
+ transaction in progress, and should not be cached."
+ -> flush this entry now */
+ /* entry reused during callback? */
+ if (entry->state == DNS_STATE_DONE) {
+ entry->state = DNS_STATE_UNUSED;
+ }
+ }
+}
+/**
+ * Receive input function for DNS response packets arriving for the dns UDP pcb.
+ */
+static void
+dns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
+{
+ u8_t i;
+ u16_t txid;
+ u16_t res_idx;
+ struct dns_hdr hdr;
+ struct dns_answer ans;
+ struct dns_query qry;
+ u16_t nquestions, nanswers;
+
+ LWIP_UNUSED_ARG(arg);
+ LWIP_UNUSED_ARG(pcb);
+ LWIP_UNUSED_ARG(port);
+
+ /* is the dns message big enough ? */
+ if (p->tot_len < (SIZEOF_DNS_HDR + SIZEOF_DNS_QUERY)) {
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: pbuf too small\n"));
+ /* free pbuf and return */
+ goto memerr;
+ }
+
+ /* copy dns payload inside static buffer for processing */
+ if (pbuf_copy_partial(p, &hdr, SIZEOF_DNS_HDR, 0) == SIZEOF_DNS_HDR) {
+ /* Match the ID in the DNS header with the name table. */
+ txid = lwip_htons(hdr.id);
+ for (i = 0; i < DNS_TABLE_SIZE; i++) {
+ const struct dns_table_entry *entry = &dns_table[i];
+ if ((entry->state == DNS_STATE_ASKING) &&
+ (entry->txid == txid)) {
+
+ /* We only care about the question(s) and the answers. The authrr
+ and the extrarr are simply discarded. */
+ nquestions = lwip_htons(hdr.numquestions);
+ nanswers = lwip_htons(hdr.numanswers);
+
+ /* Check for correct response. */
+ if ((hdr.flags1 & DNS_FLAG1_RESPONSE) == 0) {
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": not a response\n", entry->name));
+ goto memerr; /* ignore this packet */
+ }
+ if (nquestions != 1) {
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response not match to query\n", entry->name));
+ goto memerr; /* ignore this packet */
+ }
+
+#if LWIP_DNS_SUPPORT_MDNS_QUERIES
+ if (!entry->is_mdns)
+#endif /* LWIP_DNS_SUPPORT_MDNS_QUERIES */
+ {
+ /* Check whether response comes from the same network address to which the
+ question was sent. (RFC 5452) */
+ if (!ip_addr_cmp(addr, &dns_servers[entry->server_idx])) {
+ goto memerr; /* ignore this packet */
+ }
+ }
+
+ /* Check if the name in the "question" part match with the name in the entry and
+ skip it if equal. */
+ res_idx = dns_compare_name(entry->name, p, SIZEOF_DNS_HDR);
+ if (res_idx == 0xFFFF) {
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response not match to query\n", entry->name));
+ goto memerr; /* ignore this packet */
+ }
+
+ /* check if "question" part matches the request */
+ if (pbuf_copy_partial(p, &qry, SIZEOF_DNS_QUERY, res_idx) != SIZEOF_DNS_QUERY) {
+ goto memerr; /* ignore this packet */
+ }
+ if ((qry.cls != PP_HTONS(DNS_RRCLASS_IN)) ||
+ (LWIP_DNS_ADDRTYPE_IS_IPV6(entry->reqaddrtype) && (qry.type != PP_HTONS(DNS_RRTYPE_AAAA))) ||
+ (!LWIP_DNS_ADDRTYPE_IS_IPV6(entry->reqaddrtype) && (qry.type != PP_HTONS(DNS_RRTYPE_A)))) {
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response not match to query\n", entry->name));
+ goto memerr; /* ignore this packet */
+ }
+ /* skip the rest of the "question" part */
+ res_idx += SIZEOF_DNS_QUERY;
+
+ /* Check for error. If so, call callback to inform. */
+ if (hdr.flags2 & DNS_FLAG2_ERR_MASK) {
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": error in flags\n", entry->name));
+ } else {
+ while ((nanswers > 0) && (res_idx < p->tot_len)) {
+ /* skip answer resource record's host name */
+ res_idx = dns_skip_name(p, res_idx);
+ if (res_idx == 0xFFFF) {
+ goto memerr; /* ignore this packet */
+ }
+
+ /* Check for IP address type and Internet class. Others are discarded. */
+ if (pbuf_copy_partial(p, &ans, SIZEOF_DNS_ANSWER, res_idx) != SIZEOF_DNS_ANSWER) {
+ goto memerr; /* ignore this packet */
+ }
+ res_idx += SIZEOF_DNS_ANSWER;
+
+ if (ans.cls == PP_HTONS(DNS_RRCLASS_IN)) {
+#if LWIP_IPV4
+ if ((ans.type == PP_HTONS(DNS_RRTYPE_A)) && (ans.len == PP_HTONS(sizeof(ip4_addr_t)))) {
+#if LWIP_IPV4 && LWIP_IPV6
+ if (!LWIP_DNS_ADDRTYPE_IS_IPV6(entry->reqaddrtype))
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+ {
+ ip4_addr_t ip4addr;
+ /* read the IP address after answer resource record's header */
+ if (pbuf_copy_partial(p, &ip4addr, sizeof(ip4_addr_t), res_idx) != sizeof(ip4_addr_t)) {
+ goto memerr; /* ignore this packet */
+ }
+ ip_addr_copy_from_ip4(dns_table[i].ipaddr, ip4addr);
+ pbuf_free(p);
+ /* handle correct response */
+ dns_correct_response(i, lwip_ntohl(ans.ttl));
+ return;
+ }
+ }
+#endif /* LWIP_IPV4 */
+#if LWIP_IPV6
+ if ((ans.type == PP_HTONS(DNS_RRTYPE_AAAA)) && (ans.len == PP_HTONS(sizeof(ip6_addr_t)))) {
+#if LWIP_IPV4 && LWIP_IPV6
+ if (LWIP_DNS_ADDRTYPE_IS_IPV6(entry->reqaddrtype))
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+ {
+ ip6_addr_t ip6addr;
+ /* read the IP address after answer resource record's header */
+ if (pbuf_copy_partial(p, &ip6addr, sizeof(ip6_addr_t), res_idx) != sizeof(ip6_addr_t)) {
+ goto memerr; /* ignore this packet */
+ }
+ ip_addr_copy_from_ip6(dns_table[i].ipaddr, ip6addr);
+ pbuf_free(p);
+ /* handle correct response */
+ dns_correct_response(i, lwip_ntohl(ans.ttl));
+ return;
+ }
+ }
+#endif /* LWIP_IPV6 */
+ }
+ /* skip this answer */
+ if ((int)(res_idx + lwip_htons(ans.len)) > 0xFFFF) {
+ goto memerr; /* ignore this packet */
+ }
+ res_idx += lwip_htons(ans.len);
+ --nanswers;
+ }
+#if LWIP_IPV4 && LWIP_IPV6
+ if ((entry->reqaddrtype == LWIP_DNS_ADDRTYPE_IPV4_IPV6) ||
+ (entry->reqaddrtype == LWIP_DNS_ADDRTYPE_IPV6_IPV4)) {
+ if (entry->reqaddrtype == LWIP_DNS_ADDRTYPE_IPV4_IPV6) {
+ /* IPv4 failed, try IPv6 */
+ dns_table[i].reqaddrtype = LWIP_DNS_ADDRTYPE_IPV6;
+ } else {
+ /* IPv6 failed, try IPv4 */
+ dns_table[i].reqaddrtype = LWIP_DNS_ADDRTYPE_IPV4;
+ }
+ pbuf_free(p);
+ dns_table[i].state = DNS_STATE_NEW;
+ dns_check_entry(i);
+ return;
+ }
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": error in response\n", entry->name));
+ }
+ /* call callback to indicate error, clean up memory and return */
+ pbuf_free(p);
+ dns_call_found(i, NULL);
+ dns_table[i].state = DNS_STATE_UNUSED;
+ return;
+ }
+ }
+ }
+
+memerr:
+ /* deallocate memory and return */
+ pbuf_free(p);
+ return;
+}
+
+/**
+ * Queues a new hostname to resolve and sends out a DNS query for that hostname
+ *
+ * @param name the hostname that is to be queried
+ * @param hostnamelen length of the hostname
+ * @param found a callback function to be called on success, failure or timeout
+ * @param callback_arg argument to pass to the callback function
+ * @return err_t return code.
+ */
+static err_t
+dns_enqueue(const char *name, size_t hostnamelen, dns_found_callback found,
+ void *callback_arg LWIP_DNS_ADDRTYPE_ARG(u8_t dns_addrtype) LWIP_DNS_ISMDNS_ARG(u8_t is_mdns))
+{
+ u8_t i;
+ u8_t lseq, lseqi;
+ struct dns_table_entry *entry = NULL;
+ size_t namelen;
+ struct dns_req_entry* req;
+
+#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING) != 0)
+ u8_t r;
+ /* check for duplicate entries */
+ for (i = 0; i < DNS_TABLE_SIZE; i++) {
+ if ((dns_table[i].state == DNS_STATE_ASKING) &&
+ (lwip_strnicmp(name, dns_table[i].name, sizeof(dns_table[i].name)) == 0)) {
+#if LWIP_IPV4 && LWIP_IPV6
+ if (dns_table[i].reqaddrtype != dns_addrtype) {
+ /* requested address types don't match
+ this can lead to 2 concurrent requests, but mixing the address types
+ for the same host should not be that common */
+ continue;
+ }
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+ /* this is a duplicate entry, find a free request entry */
+ for (r = 0; r < DNS_MAX_REQUESTS; r++) {
+ if (dns_requests[r].found == 0) {
+ dns_requests[r].found = found;
+ dns_requests[r].arg = callback_arg;
+ dns_requests[r].dns_table_idx = i;
+ LWIP_DNS_SET_ADDRTYPE(dns_requests[r].reqaddrtype, dns_addrtype);
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": duplicate request\n", name));
+ return ERR_INPROGRESS;
+ }
+ }
+ }
+ }
+ /* no duplicate entries found */
+#endif
+
+ /* search an unused entry, or the oldest one */
+ lseq = 0;
+ lseqi = DNS_TABLE_SIZE;
+ for (i = 0; i < DNS_TABLE_SIZE; ++i) {
+ entry = &dns_table[i];
+ /* is it an unused entry ? */
+ if (entry->state == DNS_STATE_UNUSED) {
+ break;
+ }
+ /* check if this is the oldest completed entry */
+ if (entry->state == DNS_STATE_DONE) {
+ u8_t age = dns_seqno - entry->seqno;
+ if (age > lseq) {
+ lseq = age;
+ lseqi = i;
+ }
+ }
+ }
+
+ /* if we don't have found an unused entry, use the oldest completed one */
+ if (i == DNS_TABLE_SIZE) {
+ if ((lseqi >= DNS_TABLE_SIZE) || (dns_table[lseqi].state != DNS_STATE_DONE)) {
+ /* no entry can be used now, table is full */
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": DNS entries table is full\n", name));
+ return ERR_MEM;
+ } else {
+ /* use the oldest completed one */
+ i = lseqi;
+ entry = &dns_table[i];
+ }
+ }
+
+#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING) != 0)
+ /* find a free request entry */
+ req = NULL;
+ for (r = 0; r < DNS_MAX_REQUESTS; r++) {
+ if (dns_requests[r].found == NULL) {
+ req = &dns_requests[r];
+ break;
+ }
+ }
+ if (req == NULL) {
+ /* no request entry can be used now, table is full */
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": DNS request entries table is full\n", name));
+ return ERR_MEM;
+ }
+ req->dns_table_idx = i;
+#else
+ /* in this configuration, the entry index is the same as the request index */
+ req = &dns_requests[i];
+#endif
+
+ /* use this entry */
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": use DNS entry %"U16_F"\n", name, (u16_t)(i)));
+
+ /* fill the entry */
+ entry->state = DNS_STATE_NEW;
+ entry->seqno = dns_seqno;
+ LWIP_DNS_SET_ADDRTYPE(entry->reqaddrtype, dns_addrtype);
+ LWIP_DNS_SET_ADDRTYPE(req->reqaddrtype, dns_addrtype);
+ req->found = found;
+ req->arg = callback_arg;
+ namelen = LWIP_MIN(hostnamelen, DNS_MAX_NAME_LENGTH-1);
+ MEMCPY(entry->name, name, namelen);
+ entry->name[namelen] = 0;
+
+#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0)
+ entry->pcb_idx = dns_alloc_pcb();
+ if (entry->pcb_idx >= DNS_MAX_SOURCE_PORTS) {
+ /* failed to get a UDP pcb */
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": failed to allocate a pcb\n", name));
+ entry->state = DNS_STATE_UNUSED;
+ req->found = NULL;
+ return ERR_MEM;
+ }
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": use DNS pcb %"U16_F"\n", name, (u16_t)(entry->pcb_idx)));
+#endif
+
+#if LWIP_DNS_SUPPORT_MDNS_QUERIES
+ entry->is_mdns = is_mdns;
+#endif
+
+ dns_seqno++;
+
+ /* force to send query without waiting timer */
+ dns_check_entry(i);
+
+ /* dns query is enqueued */
+ return ERR_INPROGRESS;
+}
+
+/**
+ * @ingroup dns
+ * Resolve a hostname (string) into an IP address.
+ * NON-BLOCKING callback version for use with raw API!!!
+ *
+ * Returns immediately with one of err_t return codes:
+ * - ERR_OK if hostname is a valid IP address string or the host
+ * name is already in the local names table.
+ * - ERR_INPROGRESS enqueue a request to be sent to the DNS server
+ * for resolution if no errors are present.
+ * - ERR_ARG: dns client not initialized or invalid hostname
+ *
+ * @param hostname the hostname that is to be queried
+ * @param addr pointer to a ip_addr_t where to store the address if it is already
+ * cached in the dns_table (only valid if ERR_OK is returned!)
+ * @param found a callback function to be called on success, failure or timeout (only if
+ * ERR_INPROGRESS is returned!)
+ * @param callback_arg argument to pass to the callback function
+ * @return a err_t return code.
+ */
+err_t
+dns_gethostbyname(const char *hostname, ip_addr_t *addr, dns_found_callback found,
+ void *callback_arg)
+{
+ return dns_gethostbyname_addrtype(hostname, addr, found, callback_arg, LWIP_DNS_ADDRTYPE_DEFAULT);
+}
+
+/**
+ * @ingroup dns
+ * Like dns_gethostbyname, but returned address type can be controlled:
+ * @param hostname the hostname that is to be queried
+ * @param addr pointer to a ip_addr_t where to store the address if it is already
+ * cached in the dns_table (only valid if ERR_OK is returned!)
+ * @param found a callback function to be called on success, failure or timeout (only if
+ * ERR_INPROGRESS is returned!)
+ * @param callback_arg argument to pass to the callback function
+ * @param dns_addrtype - LWIP_DNS_ADDRTYPE_IPV4_IPV6: try to resolve IPv4 first, try IPv6 if IPv4 fails only
+ * - LWIP_DNS_ADDRTYPE_IPV6_IPV4: try to resolve IPv6 first, try IPv4 if IPv6 fails only
+ * - LWIP_DNS_ADDRTYPE_IPV4: try to resolve IPv4 only
+ * - LWIP_DNS_ADDRTYPE_IPV6: try to resolve IPv6 only
+ */
+err_t
+dns_gethostbyname_addrtype(const char *hostname, ip_addr_t *addr, dns_found_callback found,
+ void *callback_arg, u8_t dns_addrtype)
+{
+ size_t hostnamelen;
+#if LWIP_DNS_SUPPORT_MDNS_QUERIES
+ u8_t is_mdns;
+#endif
+ /* not initialized or no valid server yet, or invalid addr pointer
+ * or invalid hostname or invalid hostname length */
+ if ((addr == NULL) ||
+ (!hostname) || (!hostname[0])) {
+ return ERR_ARG;
+ }
+#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) == 0)
+ if (dns_pcbs[0] == NULL) {
+ return ERR_ARG;
+ }
+#endif
+ hostnamelen = strlen(hostname);
+ if (hostnamelen >= DNS_MAX_NAME_LENGTH) {
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_gethostbyname: name too long to resolve"));
+ return ERR_ARG;
+ }
+
+
+#if LWIP_HAVE_LOOPIF
+ if (strcmp(hostname, "localhost") == 0) {
+ ip_addr_set_loopback(LWIP_DNS_ADDRTYPE_IS_IPV6(dns_addrtype), addr);
+ return ERR_OK;
+ }
+#endif /* LWIP_HAVE_LOOPIF */
+
+ /* host name already in octet notation? set ip addr and return ERR_OK */
+ if (ipaddr_aton(hostname, addr)) {
+#if LWIP_IPV4 && LWIP_IPV6
+ if ((IP_IS_V6(addr) && (dns_addrtype != LWIP_DNS_ADDRTYPE_IPV4)) ||
+ (IP_IS_V4(addr) && (dns_addrtype != LWIP_DNS_ADDRTYPE_IPV6)))
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+ {
+ return ERR_OK;
+ }
+ }
+ /* already have this address cached? */
+ if (dns_lookup(hostname, addr LWIP_DNS_ADDRTYPE_ARG(dns_addrtype)) == ERR_OK) {
+ return ERR_OK;
+ }
+#if LWIP_IPV4 && LWIP_IPV6
+ if ((dns_addrtype == LWIP_DNS_ADDRTYPE_IPV4_IPV6) || (dns_addrtype == LWIP_DNS_ADDRTYPE_IPV6_IPV4)) {
+ /* fallback to 2nd IP type and try again to lookup */
+ u8_t fallback;
+ if (dns_addrtype == LWIP_DNS_ADDRTYPE_IPV4_IPV6) {
+ fallback = LWIP_DNS_ADDRTYPE_IPV6;
+ } else {
+ fallback = LWIP_DNS_ADDRTYPE_IPV4;
+ }
+ if (dns_lookup(hostname, addr LWIP_DNS_ADDRTYPE_ARG(fallback)) == ERR_OK) {
+ return ERR_OK;
+ }
+ }
+#else /* LWIP_IPV4 && LWIP_IPV6 */
+ LWIP_UNUSED_ARG(dns_addrtype);
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+
+#if LWIP_DNS_SUPPORT_MDNS_QUERIES
+ if (strstr(hostname, ".local") == &hostname[hostnamelen] - 6) {
+ is_mdns = 1;
+ } else {
+ is_mdns = 0;
+ }
+
+ if (!is_mdns)
+#endif /* LWIP_DNS_SUPPORT_MDNS_QUERIES */
+ {
+ /* prevent calling found callback if no server is set, return error instead */
+ if (ip_addr_isany_val(dns_servers[0])) {
+ return ERR_VAL;
+ }
+ }
+
+ /* queue query with specified callback */
+ return dns_enqueue(hostname, hostnamelen, found, callback_arg LWIP_DNS_ADDRTYPE_ARG(dns_addrtype)
+ LWIP_DNS_ISMDNS_ARG(is_mdns));
+}
+
+#endif /* LWIP_DNS */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/inet_chksum.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/inet_chksum.c
new file mode 100644
index 0000000..917f3e4
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/inet_chksum.c
@@ -0,0 +1,609 @@
+/**
+ * @file
+ * Incluse internet checksum functions.\n
+ *
+ * These are some reference implementations of the checksum algorithm, with the
+ * aim of being simple, correct and fully portable. Checksumming is the
+ * first thing you would want to optimize for your platform. If you create
+ * your own version, link it in and in your cc.h put:
+ *
+ * \#define LWIP_CHKSUM your_checksum_routine
+ *
+ * Or you can select from the implementations below by defining
+ * LWIP_CHKSUM_ALGORITHM to 1, 2 or 3.
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#include "lwip/inet_chksum.h"
+#include "lwip/def.h"
+#include "lwip/ip_addr.h"
+
+#include <string.h>
+
+#ifndef LWIP_CHKSUM
+# define LWIP_CHKSUM lwip_standard_chksum
+# ifndef LWIP_CHKSUM_ALGORITHM
+# define LWIP_CHKSUM_ALGORITHM 2
+# endif
+u16_t lwip_standard_chksum(const void *dataptr, int len);
+#endif
+/* If none set: */
+#ifndef LWIP_CHKSUM_ALGORITHM
+# define LWIP_CHKSUM_ALGORITHM 0
+#endif
+
+#if (LWIP_CHKSUM_ALGORITHM == 1) /* Version #1 */
+/**
+ * lwip checksum
+ *
+ * @param dataptr points to start of data to be summed at any boundary
+ * @param len length of data to be summed
+ * @return host order (!) lwip checksum (non-inverted Internet sum)
+ *
+ * @note accumulator size limits summable length to 64k
+ * @note host endianess is irrelevant (p3 RFC1071)
+ */
+u16_t
+lwip_standard_chksum(const void *dataptr, int len)
+{
+ u32_t acc;
+ u16_t src;
+ const u8_t *octetptr;
+
+ acc = 0;
+ /* dataptr may be at odd or even addresses */
+ octetptr = (const u8_t*)dataptr;
+ while (len > 1) {
+ /* declare first octet as most significant
+ thus assume network order, ignoring host order */
+ src = (*octetptr) << 8;
+ octetptr++;
+ /* declare second octet as least significant */
+ src |= (*octetptr);
+ octetptr++;
+ acc += src;
+ len -= 2;
+ }
+ if (len > 0) {
+ /* accumulate remaining octet */
+ src = (*octetptr) << 8;
+ acc += src;
+ }
+ /* add deferred carry bits */
+ acc = (acc >> 16) + (acc & 0x0000ffffUL);
+ if ((acc & 0xffff0000UL) != 0) {
+ acc = (acc >> 16) + (acc & 0x0000ffffUL);
+ }
+ /* This maybe a little confusing: reorder sum using lwip_htons()
+ instead of lwip_ntohs() since it has a little less call overhead.
+ The caller must invert bits for Internet sum ! */
+ return lwip_htons((u16_t)acc);
+}
+#endif
+
+#if (LWIP_CHKSUM_ALGORITHM == 2) /* Alternative version #2 */
+/*
+ * Curt McDowell
+ * Broadcom Corp.
+ * csm@broadcom.com
+ *
+ * IP checksum two bytes at a time with support for
+ * unaligned buffer.
+ * Works for len up to and including 0x20000.
+ * by Curt McDowell, Broadcom Corp. 12/08/2005
+ *
+ * @param dataptr points to start of data to be summed at any boundary
+ * @param len length of data to be summed
+ * @return host order (!) lwip checksum (non-inverted Internet sum)
+ */
+u16_t
+lwip_standard_chksum(const void *dataptr, int len)
+{
+ const u8_t *pb = (const u8_t *)dataptr;
+ const u16_t *ps;
+ u16_t t = 0;
+ u32_t sum = 0;
+ int odd = ((mem_ptr_t)pb & 1);
+
+ /* Get aligned to u16_t */
+ if (odd && len > 0) {
+ ((u8_t *)&t)[1] = *pb++;
+ len--;
+ }
+
+ /* Add the bulk of the data */
+ ps = (const u16_t *)(const void *)pb;
+ while (len > 1) {
+ sum += *ps++;
+ len -= 2;
+ }
+
+ /* Consume left-over byte, if any */
+ if (len > 0) {
+ ((u8_t *)&t)[0] = *(const u8_t *)ps;
+ }
+
+ /* Add end bytes */
+ sum += t;
+
+ /* Fold 32-bit sum to 16 bits
+ calling this twice is probably faster than if statements... */
+ sum = FOLD_U32T(sum);
+ sum = FOLD_U32T(sum);
+
+ /* Swap if alignment was odd */
+ if (odd) {
+ sum = SWAP_BYTES_IN_WORD(sum);
+ }
+
+ return (u16_t)sum;
+}
+#endif
+
+#if (LWIP_CHKSUM_ALGORITHM == 3) /* Alternative version #3 */
+/**
+ * An optimized checksum routine. Basically, it uses loop-unrolling on
+ * the checksum loop, treating the head and tail bytes specially, whereas
+ * the inner loop acts on 8 bytes at a time.
+ *
+ * @arg start of buffer to be checksummed. May be an odd byte address.
+ * @len number of bytes in the buffer to be checksummed.
+ * @return host order (!) lwip checksum (non-inverted Internet sum)
+ *
+ * by Curt McDowell, Broadcom Corp. December 8th, 2005
+ */
+u16_t
+lwip_standard_chksum(const void *dataptr, int len)
+{
+ const u8_t *pb = (const u8_t *)dataptr;
+ const u16_t *ps;
+ u16_t t = 0;
+ const u32_t *pl;
+ u32_t sum = 0, tmp;
+ /* starts at odd byte address? */
+ int odd = ((mem_ptr_t)pb & 1);
+
+ if (odd && len > 0) {
+ ((u8_t *)&t)[1] = *pb++;
+ len--;
+ }
+
+ ps = (const u16_t *)(const void*)pb;
+
+ if (((mem_ptr_t)ps & 3) && len > 1) {
+ sum += *ps++;
+ len -= 2;
+ }
+
+ pl = (const u32_t *)(const void*)ps;
+
+ while (len > 7) {
+ tmp = sum + *pl++; /* ping */
+ if (tmp < sum) {
+ tmp++; /* add back carry */
+ }
+
+ sum = tmp + *pl++; /* pong */
+ if (sum < tmp) {
+ sum++; /* add back carry */
+ }
+
+ len -= 8;
+ }
+
+ /* make room in upper bits */
+ sum = FOLD_U32T(sum);
+
+ ps = (const u16_t *)pl;
+
+ /* 16-bit aligned word remaining? */
+ while (len > 1) {
+ sum += *ps++;
+ len -= 2;
+ }
+
+ /* dangling tail byte remaining? */
+ if (len > 0) { /* include odd byte */
+ ((u8_t *)&t)[0] = *(const u8_t *)ps;
+ }
+
+ sum += t; /* add end bytes */
+
+ /* Fold 32-bit sum to 16 bits
+ calling this twice is probably faster than if statements... */
+ sum = FOLD_U32T(sum);
+ sum = FOLD_U32T(sum);
+
+ if (odd) {
+ sum = SWAP_BYTES_IN_WORD(sum);
+ }
+
+ return (u16_t)sum;
+}
+#endif
+
+/** Parts of the pseudo checksum which are common to IPv4 and IPv6 */
+static u16_t
+inet_cksum_pseudo_base(struct pbuf *p, u8_t proto, u16_t proto_len, u32_t acc)
+{
+ struct pbuf *q;
+ u8_t swapped = 0;
+
+ /* iterate through all pbuf in chain */
+ for (q = p; q != NULL; q = q->next) {
+ LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): checksumming pbuf %p (has next %p) \n",
+ (void *)q, (void *)q->next));
+ acc += LWIP_CHKSUM(q->payload, q->len);
+ /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): unwrapped lwip_chksum()=%"X32_F" \n", acc));*/
+ /* just executing this next line is probably faster that the if statement needed
+ to check whether we really need to execute it, and does no harm */
+ acc = FOLD_U32T(acc);
+ if (q->len % 2 != 0) {
+ swapped = 1 - swapped;
+ acc = SWAP_BYTES_IN_WORD(acc);
+ }
+ /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): wrapped lwip_chksum()=%"X32_F" \n", acc));*/
+ }
+
+ if (swapped) {
+ acc = SWAP_BYTES_IN_WORD(acc);
+ }
+
+ acc += (u32_t)lwip_htons((u16_t)proto);
+ acc += (u32_t)lwip_htons(proto_len);
+
+ /* Fold 32-bit sum to 16 bits
+ calling this twice is probably faster than if statements... */
+ acc = FOLD_U32T(acc);
+ acc = FOLD_U32T(acc);
+ LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): pbuf chain lwip_chksum()=%"X32_F"\n", acc));
+ return (u16_t)~(acc & 0xffffUL);
+}
+
+#if LWIP_IPV4
+/* inet_chksum_pseudo:
+ *
+ * Calculates the IPv4 pseudo Internet checksum used by TCP and UDP for a pbuf chain.
+ * IP addresses are expected to be in network byte order.
+ *
+ * @param p chain of pbufs over that a checksum should be calculated (ip data part)
+ * @param src source ip address (used for checksum of pseudo header)
+ * @param dst destination ip address (used for checksum of pseudo header)
+ * @param proto ip protocol (used for checksum of pseudo header)
+ * @param proto_len length of the ip data part (used for checksum of pseudo header)
+ * @return checksum (as u16_t) to be saved directly in the protocol header
+ */
+u16_t
+inet_chksum_pseudo(struct pbuf *p, u8_t proto, u16_t proto_len,
+ const ip4_addr_t *src, const ip4_addr_t *dest)
+{
+ u32_t acc;
+ u32_t addr;
+
+ addr = ip4_addr_get_u32(src);
+ acc = (addr & 0xffffUL);
+ acc += ((addr >> 16) & 0xffffUL);
+ addr = ip4_addr_get_u32(dest);
+ acc += (addr & 0xffffUL);
+ acc += ((addr >> 16) & 0xffffUL);
+ /* fold down to 16 bits */
+ acc = FOLD_U32T(acc);
+ acc = FOLD_U32T(acc);
+
+ return inet_cksum_pseudo_base(p, proto, proto_len, acc);
+}
+#endif /* LWIP_IPV4 */
+
+#if LWIP_IPV6
+/**
+ * Calculates the checksum with IPv6 pseudo header used by TCP and UDP for a pbuf chain.
+ * IPv6 addresses are expected to be in network byte order.
+ *
+ * @param p chain of pbufs over that a checksum should be calculated (ip data part)
+ * @param proto ipv6 protocol/next header (used for checksum of pseudo header)
+ * @param proto_len length of the ipv6 payload (used for checksum of pseudo header)
+ * @param src source ipv6 address (used for checksum of pseudo header)
+ * @param dest destination ipv6 address (used for checksum of pseudo header)
+ * @return checksum (as u16_t) to be saved directly in the protocol header
+ */
+u16_t
+ip6_chksum_pseudo(struct pbuf *p, u8_t proto, u16_t proto_len,
+ const ip6_addr_t *src, const ip6_addr_t *dest)
+{
+ u32_t acc = 0;
+ u32_t addr;
+ u8_t addr_part;
+
+ for (addr_part = 0; addr_part < 4; addr_part++) {
+ addr = src->addr[addr_part];
+ acc += (addr & 0xffffUL);
+ acc += ((addr >> 16) & 0xffffUL);
+ addr = dest->addr[addr_part];
+ acc += (addr & 0xffffUL);
+ acc += ((addr >> 16) & 0xffffUL);
+ }
+ /* fold down to 16 bits */
+ acc = FOLD_U32T(acc);
+ acc = FOLD_U32T(acc);
+
+ return inet_cksum_pseudo_base(p, proto, proto_len, acc);
+}
+#endif /* LWIP_IPV6 */
+
+/* ip_chksum_pseudo:
+ *
+ * Calculates the IPv4 or IPv6 pseudo Internet checksum used by TCP and UDP for a pbuf chain.
+ * IP addresses are expected to be in network byte order.
+ *
+ * @param p chain of pbufs over that a checksum should be calculated (ip data part)
+ * @param src source ip address (used for checksum of pseudo header)
+ * @param dst destination ip address (used for checksum of pseudo header)
+ * @param proto ip protocol (used for checksum of pseudo header)
+ * @param proto_len length of the ip data part (used for checksum of pseudo header)
+ * @return checksum (as u16_t) to be saved directly in the protocol header
+ */
+u16_t
+ip_chksum_pseudo(struct pbuf *p, u8_t proto, u16_t proto_len,
+ const ip_addr_t *src, const ip_addr_t *dest)
+{
+#if LWIP_IPV6
+ if (IP_IS_V6(dest)) {
+ return ip6_chksum_pseudo(p, proto, proto_len, ip_2_ip6(src), ip_2_ip6(dest));
+ }
+#endif /* LWIP_IPV6 */
+#if LWIP_IPV4 && LWIP_IPV6
+ else
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+#if LWIP_IPV4
+ {
+ return inet_chksum_pseudo(p, proto, proto_len, ip_2_ip4(src), ip_2_ip4(dest));
+ }
+#endif /* LWIP_IPV4 */
+}
+
+/** Parts of the pseudo checksum which are common to IPv4 and IPv6 */
+static u16_t
+inet_cksum_pseudo_partial_base(struct pbuf *p, u8_t proto, u16_t proto_len,
+ u16_t chksum_len, u32_t acc)
+{
+ struct pbuf *q;
+ u8_t swapped = 0;
+ u16_t chklen;
+
+ /* iterate through all pbuf in chain */
+ for (q = p; (q != NULL) && (chksum_len > 0); q = q->next) {
+ LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): checksumming pbuf %p (has next %p) \n",
+ (void *)q, (void *)q->next));
+ chklen = q->len;
+ if (chklen > chksum_len) {
+ chklen = chksum_len;
+ }
+ acc += LWIP_CHKSUM(q->payload, chklen);
+ chksum_len -= chklen;
+ LWIP_ASSERT("delete me", chksum_len < 0x7fff);
+ /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): unwrapped lwip_chksum()=%"X32_F" \n", acc));*/
+ /* fold the upper bit down */
+ acc = FOLD_U32T(acc);
+ if (q->len % 2 != 0) {
+ swapped = 1 - swapped;
+ acc = SWAP_BYTES_IN_WORD(acc);
+ }
+ /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): wrapped lwip_chksum()=%"X32_F" \n", acc));*/
+ }
+
+ if (swapped) {
+ acc = SWAP_BYTES_IN_WORD(acc);
+ }
+
+ acc += (u32_t)lwip_htons((u16_t)proto);
+ acc += (u32_t)lwip_htons(proto_len);
+
+ /* Fold 32-bit sum to 16 bits
+ calling this twice is probably faster than if statements... */
+ acc = FOLD_U32T(acc);
+ acc = FOLD_U32T(acc);
+ LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): pbuf chain lwip_chksum()=%"X32_F"\n", acc));
+ return (u16_t)~(acc & 0xffffUL);
+}
+
+#if LWIP_IPV4
+/* inet_chksum_pseudo_partial:
+ *
+ * Calculates the IPv4 pseudo Internet checksum used by TCP and UDP for a pbuf chain.
+ * IP addresses are expected to be in network byte order.
+ *
+ * @param p chain of pbufs over that a checksum should be calculated (ip data part)
+ * @param src source ip address (used for checksum of pseudo header)
+ * @param dst destination ip address (used for checksum of pseudo header)
+ * @param proto ip protocol (used for checksum of pseudo header)
+ * @param proto_len length of the ip data part (used for checksum of pseudo header)
+ * @return checksum (as u16_t) to be saved directly in the protocol header
+ */
+u16_t
+inet_chksum_pseudo_partial(struct pbuf *p, u8_t proto, u16_t proto_len,
+ u16_t chksum_len, const ip4_addr_t *src, const ip4_addr_t *dest)
+{
+ u32_t acc;
+ u32_t addr;
+
+ addr = ip4_addr_get_u32(src);
+ acc = (addr & 0xffffUL);
+ acc += ((addr >> 16) & 0xffffUL);
+ addr = ip4_addr_get_u32(dest);
+ acc += (addr & 0xffffUL);
+ acc += ((addr >> 16) & 0xffffUL);
+ /* fold down to 16 bits */
+ acc = FOLD_U32T(acc);
+ acc = FOLD_U32T(acc);
+
+ return inet_cksum_pseudo_partial_base(p, proto, proto_len, chksum_len, acc);
+}
+#endif /* LWIP_IPV4 */
+
+#if LWIP_IPV6
+/**
+ * Calculates the checksum with IPv6 pseudo header used by TCP and UDP for a pbuf chain.
+ * IPv6 addresses are expected to be in network byte order. Will only compute for a
+ * portion of the payload.
+ *
+ * @param p chain of pbufs over that a checksum should be calculated (ip data part)
+ * @param proto ipv6 protocol/next header (used for checksum of pseudo header)
+ * @param proto_len length of the ipv6 payload (used for checksum of pseudo header)
+ * @param chksum_len number of payload bytes used to compute chksum
+ * @param src source ipv6 address (used for checksum of pseudo header)
+ * @param dest destination ipv6 address (used for checksum of pseudo header)
+ * @return checksum (as u16_t) to be saved directly in the protocol header
+ */
+u16_t
+ip6_chksum_pseudo_partial(struct pbuf *p, u8_t proto, u16_t proto_len,
+ u16_t chksum_len, const ip6_addr_t *src, const ip6_addr_t *dest)
+{
+ u32_t acc = 0;
+ u32_t addr;
+ u8_t addr_part;
+
+ for (addr_part = 0; addr_part < 4; addr_part++) {
+ addr = src->addr[addr_part];
+ acc += (addr & 0xffffUL);
+ acc += ((addr >> 16) & 0xffffUL);
+ addr = dest->addr[addr_part];
+ acc += (addr & 0xffffUL);
+ acc += ((addr >> 16) & 0xffffUL);
+ }
+ /* fold down to 16 bits */
+ acc = FOLD_U32T(acc);
+ acc = FOLD_U32T(acc);
+
+ return inet_cksum_pseudo_partial_base(p, proto, proto_len, chksum_len, acc);
+}
+#endif /* LWIP_IPV6 */
+
+/* ip_chksum_pseudo_partial:
+ *
+ * Calculates the IPv4 or IPv6 pseudo Internet checksum used by TCP and UDP for a pbuf chain.
+ *
+ * @param p chain of pbufs over that a checksum should be calculated (ip data part)
+ * @param src source ip address (used for checksum of pseudo header)
+ * @param dst destination ip address (used for checksum of pseudo header)
+ * @param proto ip protocol (used for checksum of pseudo header)
+ * @param proto_len length of the ip data part (used for checksum of pseudo header)
+ * @return checksum (as u16_t) to be saved directly in the protocol header
+ */
+u16_t
+ip_chksum_pseudo_partial(struct pbuf *p, u8_t proto, u16_t proto_len,
+ u16_t chksum_len, const ip_addr_t *src, const ip_addr_t *dest)
+{
+#if LWIP_IPV6
+ if (IP_IS_V6(dest)) {
+ return ip6_chksum_pseudo_partial(p, proto, proto_len, chksum_len, ip_2_ip6(src), ip_2_ip6(dest));
+ }
+#endif /* LWIP_IPV6 */
+#if LWIP_IPV4 && LWIP_IPV6
+ else
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+#if LWIP_IPV4
+ {
+ return inet_chksum_pseudo_partial(p, proto, proto_len, chksum_len, ip_2_ip4(src), ip_2_ip4(dest));
+ }
+#endif /* LWIP_IPV4 */
+}
+
+/* inet_chksum:
+ *
+ * Calculates the Internet checksum over a portion of memory. Used primarily for IP
+ * and ICMP.
+ *
+ * @param dataptr start of the buffer to calculate the checksum (no alignment needed)
+ * @param len length of the buffer to calculate the checksum
+ * @return checksum (as u16_t) to be saved directly in the protocol header
+ */
+
+u16_t
+inet_chksum(const void *dataptr, u16_t len)
+{
+ return (u16_t)~(unsigned int)LWIP_CHKSUM(dataptr, len);
+}
+
+/**
+ * Calculate a checksum over a chain of pbufs (without pseudo-header, much like
+ * inet_chksum only pbufs are used).
+ *
+ * @param p pbuf chain over that the checksum should be calculated
+ * @return checksum (as u16_t) to be saved directly in the protocol header
+ */
+u16_t
+inet_chksum_pbuf(struct pbuf *p)
+{
+ u32_t acc;
+ struct pbuf *q;
+ u8_t swapped;
+
+ acc = 0;
+ swapped = 0;
+ for (q = p; q != NULL; q = q->next) {
+ acc += LWIP_CHKSUM(q->payload, q->len);
+ acc = FOLD_U32T(acc);
+ if (q->len % 2 != 0) {
+ swapped = 1 - swapped;
+ acc = SWAP_BYTES_IN_WORD(acc);
+ }
+ }
+
+ if (swapped) {
+ acc = SWAP_BYTES_IN_WORD(acc);
+ }
+ return (u16_t)~(acc & 0xffffUL);
+}
+
+/* These are some implementations for LWIP_CHKSUM_COPY, which copies data
+ * like MEMCPY but generates a checksum at the same time. Since this is a
+ * performance-sensitive function, you might want to create your own version
+ * in assembly targeted at your hardware by defining it in lwipopts.h:
+ * #define LWIP_CHKSUM_COPY(dst, src, len) your_chksum_copy(dst, src, len)
+ */
+
+#if (LWIP_CHKSUM_COPY_ALGORITHM == 1) /* Version #1 */
+/** Safe but slow: first call MEMCPY, then call LWIP_CHKSUM.
+ * For architectures with big caches, data might still be in cache when
+ * generating the checksum after copying.
+ */
+u16_t
+lwip_chksum_copy(void *dst, const void *src, u16_t len)
+{
+ MEMCPY(dst, src, len);
+ return LWIP_CHKSUM(dst, len);
+}
+#endif /* (LWIP_CHKSUM_COPY_ALGORITHM == 1) */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/init.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/init.c
new file mode 100644
index 0000000..3131461
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/init.c
@@ -0,0 +1,385 @@
+/**
+ * @file
+ * Modules initialization
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ */
+
+#include "lwip/opt.h"
+
+#include "lwip/init.h"
+#include "lwip/stats.h"
+#include "lwip/sys.h"
+#include "lwip/mem.h"
+#include "lwip/memp.h"
+#include "lwip/pbuf.h"
+#include "lwip/netif.h"
+#include "lwip/sockets.h"
+#include "lwip/ip.h"
+#include "lwip/raw.h"
+#include "lwip/udp.h"
+#include "lwip/priv/tcp_priv.h"
+#include "lwip/igmp.h"
+#include "lwip/dns.h"
+#include "lwip/timeouts.h"
+#include "lwip/etharp.h"
+#include "lwip/ip6.h"
+#include "lwip/nd6.h"
+#include "lwip/mld6.h"
+#include "lwip/api.h"
+
+#include "netif/ppp/ppp_opts.h"
+#include "netif/ppp/ppp_impl.h"
+
+#ifndef LWIP_SKIP_PACKING_CHECK
+
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct packed_struct_test
+{
+ PACK_STRUCT_FLD_8(u8_t dummy1);
+ PACK_STRUCT_FIELD(u32_t dummy2);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+#define PACKED_STRUCT_TEST_EXPECTED_SIZE 5
+
+#endif
+
+/* Compile-time sanity checks for configuration errors.
+ * These can be done independently of LWIP_DEBUG, without penalty.
+ */
+#ifndef BYTE_ORDER
+ #error "BYTE_ORDER is not defined, you have to define it in your cc.h"
+#endif
+#if (!IP_SOF_BROADCAST && IP_SOF_BROADCAST_RECV)
+ #error "If you want to use broadcast filter per pcb on recv operations, you have to define IP_SOF_BROADCAST=1 in your lwipopts.h"
+#endif
+#if (!LWIP_UDP && LWIP_UDPLITE)
+ #error "If you want to use UDP Lite, you have to define LWIP_UDP=1 in your lwipopts.h"
+#endif
+#if (!LWIP_UDP && LWIP_DHCP)
+ #error "If you want to use DHCP, you have to define LWIP_UDP=1 in your lwipopts.h"
+#endif
+#if (!LWIP_UDP && LWIP_MULTICAST_TX_OPTIONS)
+ #error "If you want to use IGMP/LWIP_MULTICAST_TX_OPTIONS, you have to define LWIP_UDP=1 in your lwipopts.h"
+#endif
+#if (!LWIP_UDP && LWIP_DNS)
+ #error "If you want to use DNS, you have to define LWIP_UDP=1 in your lwipopts.h"
+#endif
+#if !MEMP_MEM_MALLOC /* MEMP_NUM_* checks are disabled when not using the pool allocator */
+#if (LWIP_ARP && ARP_QUEUEING && (MEMP_NUM_ARP_QUEUE<=0))
+ #error "If you want to use ARP Queueing, you have to define MEMP_NUM_ARP_QUEUE>=1 in your lwipopts.h"
+#endif
+#if (LWIP_RAW && (MEMP_NUM_RAW_PCB<=0))
+ #error "If you want to use RAW, you have to define MEMP_NUM_RAW_PCB>=1 in your lwipopts.h"
+#endif
+#if (LWIP_UDP && (MEMP_NUM_UDP_PCB<=0))
+ #error "If you want to use UDP, you have to define MEMP_NUM_UDP_PCB>=1 in your lwipopts.h"
+#endif
+#if (LWIP_TCP && (MEMP_NUM_TCP_PCB<=0))
+ #error "If you want to use TCP, you have to define MEMP_NUM_TCP_PCB>=1 in your lwipopts.h"
+#endif
+#if (LWIP_IGMP && (MEMP_NUM_IGMP_GROUP<=1))
+ #error "If you want to use IGMP, you have to define MEMP_NUM_IGMP_GROUP>1 in your lwipopts.h"
+#endif
+#if (LWIP_IGMP && !LWIP_MULTICAST_TX_OPTIONS)
+ #error "If you want to use IGMP, you have to define LWIP_MULTICAST_TX_OPTIONS==1 in your lwipopts.h"
+#endif
+#if (LWIP_IGMP && !LWIP_IPV4)
+ #error "IGMP needs LWIP_IPV4 enabled in your lwipopts.h"
+#endif
+#if (LWIP_MULTICAST_TX_OPTIONS && !LWIP_IPV4)
+ #error "LWIP_MULTICAST_TX_OPTIONS needs LWIP_IPV4 enabled in your lwipopts.h"
+#endif
+#if ((LWIP_NETCONN || LWIP_SOCKET) && (MEMP_NUM_TCPIP_MSG_API<=0))
+ #error "If you want to use Sequential API, you have to define MEMP_NUM_TCPIP_MSG_API>=1 in your lwipopts.h"
+#endif
+/* There must be sufficient timeouts, taking into account requirements of the subsystems. */
+#if LWIP_TIMERS && (MEMP_NUM_SYS_TIMEOUT < (LWIP_TCP + IP_REASSEMBLY + LWIP_ARP + (2*LWIP_DHCP) + LWIP_AUTOIP + LWIP_IGMP + LWIP_DNS + PPP_SUPPORT + (LWIP_IPV6 ? (1 + LWIP_IPV6_REASS + LWIP_IPV6_MLD) : 0)))
+ #error "MEMP_NUM_SYS_TIMEOUT is too low to accomodate all required timeouts"
+#endif
+#if (IP_REASSEMBLY && (MEMP_NUM_REASSDATA > IP_REASS_MAX_PBUFS))
+ #error "MEMP_NUM_REASSDATA > IP_REASS_MAX_PBUFS doesn't make sense since each struct ip_reassdata must hold 2 pbufs at least!"
+#endif
+#endif /* !MEMP_MEM_MALLOC */
+#if LWIP_WND_SCALE
+#if (LWIP_TCP && (TCP_WND > 0xffffffff))
+ #error "If you want to use TCP, TCP_WND must fit in an u32_t, so, you have to reduce it in your lwipopts.h"
+#endif
+#if (LWIP_TCP && (TCP_RCV_SCALE > 14))
+ #error "The maximum valid window scale value is 14!"
+#endif
+#if (LWIP_TCP && (TCP_WND > (0xFFFFU << TCP_RCV_SCALE)))
+ #error "TCP_WND is bigger than the configured LWIP_WND_SCALE allows!"
+#endif
+#if (LWIP_TCP && ((TCP_WND >> TCP_RCV_SCALE) == 0))
+ #error "TCP_WND is too small for the configured LWIP_WND_SCALE (results in zero window)!"
+#endif
+#else /* LWIP_WND_SCALE */
+#if (LWIP_TCP && (TCP_WND > 0xffff))
+ #error "If you want to use TCP, TCP_WND must fit in an u16_t, so, you have to reduce it in your lwipopts.h (or enable window scaling)"
+#endif
+#endif /* LWIP_WND_SCALE */
+#if (LWIP_TCP && (TCP_SND_QUEUELEN > 0xffff))
+ #error "If you want to use TCP, TCP_SND_QUEUELEN must fit in an u16_t, so, you have to reduce it in your lwipopts.h"
+#endif
+#if (LWIP_TCP && (TCP_SND_QUEUELEN < 2))
+ #error "TCP_SND_QUEUELEN must be at least 2 for no-copy TCP writes to work"
+#endif
+#if (LWIP_TCP && ((TCP_MAXRTX > 12) || (TCP_SYNMAXRTX > 12)))
+ #error "If you want to use TCP, TCP_MAXRTX and TCP_SYNMAXRTX must less or equal to 12 (due to tcp_backoff table), so, you have to reduce them in your lwipopts.h"
+#endif
+#if (LWIP_TCP && TCP_LISTEN_BACKLOG && ((TCP_DEFAULT_LISTEN_BACKLOG < 0) || (TCP_DEFAULT_LISTEN_BACKLOG > 0xff)))
+ #error "If you want to use TCP backlog, TCP_DEFAULT_LISTEN_BACKLOG must fit into an u8_t"
+#endif
+#if (LWIP_NETIF_API && (NO_SYS==1))
+ #error "If you want to use NETIF API, you have to define NO_SYS=0 in your lwipopts.h"
+#endif
+#if ((LWIP_SOCKET || LWIP_NETCONN) && (NO_SYS==1))
+ #error "If you want to use Sequential API, you have to define NO_SYS=0 in your lwipopts.h"
+#endif
+#if (LWIP_PPP_API && (NO_SYS==1))
+ #error "If you want to use PPP API, you have to define NO_SYS=0 in your lwipopts.h"
+#endif
+#if (LWIP_PPP_API && (PPP_SUPPORT==0))
+ #error "If you want to use PPP API, you have to enable PPP_SUPPORT in your lwipopts.h"
+#endif
+#if (((!LWIP_DHCP) || (!LWIP_AUTOIP)) && LWIP_DHCP_AUTOIP_COOP)
+ #error "If you want to use DHCP/AUTOIP cooperation mode, you have to define LWIP_DHCP=1 and LWIP_AUTOIP=1 in your lwipopts.h"
+#endif
+#if (((!LWIP_DHCP) || (!LWIP_ARP)) && DHCP_DOES_ARP_CHECK)
+ #error "If you want to use DHCP ARP checking, you have to define LWIP_DHCP=1 and LWIP_ARP=1 in your lwipopts.h"
+#endif
+#if (!LWIP_ARP && LWIP_AUTOIP)
+ #error "If you want to use AUTOIP, you have to define LWIP_ARP=1 in your lwipopts.h"
+#endif
+#if (LWIP_TCP && ((LWIP_EVENT_API && LWIP_CALLBACK_API) || (!LWIP_EVENT_API && !LWIP_CALLBACK_API)))
+ #error "One and exactly one of LWIP_EVENT_API and LWIP_CALLBACK_API has to be enabled in your lwipopts.h"
+#endif
+#if (MEM_LIBC_MALLOC && MEM_USE_POOLS)
+ #error "MEM_LIBC_MALLOC and MEM_USE_POOLS may not both be simultaneously enabled in your lwipopts.h"
+#endif
+#if (MEM_USE_POOLS && !MEMP_USE_CUSTOM_POOLS)
+ #error "MEM_USE_POOLS requires custom pools (MEMP_USE_CUSTOM_POOLS) to be enabled in your lwipopts.h"
+#endif
+#if (PBUF_POOL_BUFSIZE <= MEM_ALIGNMENT)
+ #error "PBUF_POOL_BUFSIZE must be greater than MEM_ALIGNMENT or the offset may take the full first pbuf"
+#endif
+#if (DNS_LOCAL_HOSTLIST && !DNS_LOCAL_HOSTLIST_IS_DYNAMIC && !(defined(DNS_LOCAL_HOSTLIST_INIT)))
+ #error "you have to define define DNS_LOCAL_HOSTLIST_INIT {{'host1', 0x123}, {'host2', 0x234}} to initialize DNS_LOCAL_HOSTLIST"
+#endif
+#if PPP_SUPPORT && !PPPOS_SUPPORT && !PPPOE_SUPPORT && !PPPOL2TP_SUPPORT
+ #error "PPP_SUPPORT needs at least one of PPPOS_SUPPORT, PPPOE_SUPPORT or PPPOL2TP_SUPPORT turned on"
+#endif
+#if PPP_SUPPORT && !PPP_IPV4_SUPPORT && !PPP_IPV6_SUPPORT
+ #error "PPP_SUPPORT needs PPP_IPV4_SUPPORT and/or PPP_IPV6_SUPPORT turned on"
+#endif
+#if PPP_SUPPORT && PPP_IPV4_SUPPORT && !LWIP_IPV4
+ #error "PPP_IPV4_SUPPORT needs LWIP_IPV4 turned on"
+#endif
+#if PPP_SUPPORT && PPP_IPV6_SUPPORT && !LWIP_IPV6
+ #error "PPP_IPV6_SUPPORT needs LWIP_IPV6 turned on"
+#endif
+#if !LWIP_ETHERNET && (LWIP_ARP || PPPOE_SUPPORT)
+ #error "LWIP_ETHERNET needs to be turned on for LWIP_ARP or PPPOE_SUPPORT"
+#endif
+#if LWIP_TCPIP_CORE_LOCKING_INPUT && !LWIP_TCPIP_CORE_LOCKING
+ #error "When using LWIP_TCPIP_CORE_LOCKING_INPUT, LWIP_TCPIP_CORE_LOCKING must be enabled, too"
+#endif
+#if LWIP_TCP && LWIP_NETIF_TX_SINGLE_PBUF && !TCP_OVERSIZE
+ #error "LWIP_NETIF_TX_SINGLE_PBUF needs TCP_OVERSIZE enabled to create single-pbuf TCP packets"
+#endif
+#if LWIP_NETCONN && LWIP_TCP
+#if NETCONN_COPY != TCP_WRITE_FLAG_COPY
+ #error "NETCONN_COPY != TCP_WRITE_FLAG_COPY"
+#endif
+#if NETCONN_MORE != TCP_WRITE_FLAG_MORE
+ #error "NETCONN_MORE != TCP_WRITE_FLAG_MORE"
+#endif
+#endif /* LWIP_NETCONN && LWIP_TCP */
+#if LWIP_SOCKET
+/* Check that the SO_* socket options and SOF_* lwIP-internal flags match */
+#if SO_REUSEADDR != SOF_REUSEADDR
+ #error "WARNING: SO_REUSEADDR != SOF_REUSEADDR"
+#endif
+#if SO_KEEPALIVE != SOF_KEEPALIVE
+ #error "WARNING: SO_KEEPALIVE != SOF_KEEPALIVE"
+#endif
+#if SO_BROADCAST != SOF_BROADCAST
+ #error "WARNING: SO_BROADCAST != SOF_BROADCAST"
+#endif
+#endif /* LWIP_SOCKET */
+
+
+/* Compile-time checks for deprecated options.
+ */
+#ifdef MEMP_NUM_TCPIP_MSG
+ #error "MEMP_NUM_TCPIP_MSG option is deprecated. Remove it from your lwipopts.h."
+#endif
+#ifdef TCP_REXMIT_DEBUG
+ #error "TCP_REXMIT_DEBUG option is deprecated. Remove it from your lwipopts.h."
+#endif
+#ifdef RAW_STATS
+ #error "RAW_STATS option is deprecated. Remove it from your lwipopts.h."
+#endif
+#ifdef ETHARP_QUEUE_FIRST
+ #error "ETHARP_QUEUE_FIRST option is deprecated. Remove it from your lwipopts.h."
+#endif
+#ifdef ETHARP_ALWAYS_INSERT
+ #error "ETHARP_ALWAYS_INSERT option is deprecated. Remove it from your lwipopts.h."
+#endif
+#if !NO_SYS && LWIP_TCPIP_CORE_LOCKING && LWIP_COMPAT_MUTEX && !defined(LWIP_COMPAT_MUTEX_ALLOWED)
+ #error "LWIP_COMPAT_MUTEX cannot prevent priority inversion. It is recommended to implement priority-aware mutexes. (Define LWIP_COMPAT_MUTEX_ALLOWED to disable this error.)"
+#endif
+
+#ifndef LWIP_DISABLE_TCP_SANITY_CHECKS
+#define LWIP_DISABLE_TCP_SANITY_CHECKS 0
+#endif
+#ifndef LWIP_DISABLE_MEMP_SANITY_CHECKS
+#define LWIP_DISABLE_MEMP_SANITY_CHECKS 0
+#endif
+
+/* MEMP sanity checks */
+#if MEMP_MEM_MALLOC
+#if !LWIP_DISABLE_MEMP_SANITY_CHECKS
+#if LWIP_NETCONN || LWIP_SOCKET
+#if !MEMP_NUM_NETCONN && LWIP_SOCKET
+#error "lwip_sanity_check: WARNING: MEMP_NUM_NETCONN cannot be 0 when using sockets!"
+#endif
+#else /* MEMP_MEM_MALLOC */
+#if MEMP_NUM_NETCONN > (MEMP_NUM_TCP_PCB+MEMP_NUM_TCP_PCB_LISTEN+MEMP_NUM_UDP_PCB+MEMP_NUM_RAW_PCB)
+#error "lwip_sanity_check: WARNING: MEMP_NUM_NETCONN should be less than the sum of MEMP_NUM_{TCP,RAW,UDP}_PCB+MEMP_NUM_TCP_PCB_LISTEN. If you know what you are doing, define LWIP_DISABLE_MEMP_SANITY_CHECKS to 1 to disable this error."
+#endif
+#endif /* LWIP_NETCONN || LWIP_SOCKET */
+#endif /* !LWIP_DISABLE_MEMP_SANITY_CHECKS */
+#if MEM_USE_POOLS
+#error "MEMP_MEM_MALLOC and MEM_USE_POOLS cannot be enabled at the same time"
+#endif
+#ifdef LWIP_HOOK_MEMP_AVAILABLE
+#error "LWIP_HOOK_MEMP_AVAILABLE doesn't make sense with MEMP_MEM_MALLOC"
+#endif
+#endif /* MEMP_MEM_MALLOC */
+
+/* TCP sanity checks */
+#if !LWIP_DISABLE_TCP_SANITY_CHECKS
+#if LWIP_TCP
+#if !MEMP_MEM_MALLOC && (MEMP_NUM_TCP_SEG < TCP_SND_QUEUELEN)
+ #error "lwip_sanity_check: WARNING: MEMP_NUM_TCP_SEG should be at least as big as TCP_SND_QUEUELEN. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error."
+#endif
+#if TCP_SND_BUF < (2 * TCP_MSS)
+ #error "lwip_sanity_check: WARNING: TCP_SND_BUF must be at least as much as (2 * TCP_MSS) for things to work smoothly. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error."
+#endif
+#if TCP_SND_QUEUELEN < (2 * (TCP_SND_BUF / TCP_MSS))
+ #error "lwip_sanity_check: WARNING: TCP_SND_QUEUELEN must be at least as much as (2 * TCP_SND_BUF/TCP_MSS) for things to work. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error."
+#endif
+#if TCP_SNDLOWAT >= TCP_SND_BUF
+ #error "lwip_sanity_check: WARNING: TCP_SNDLOWAT must be less than TCP_SND_BUF. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error."
+#endif
+#if TCP_SNDLOWAT >= (0xFFFF - (4 * TCP_MSS))
+ #error "lwip_sanity_check: WARNING: TCP_SNDLOWAT must at least be 4*MSS below u16_t overflow!"
+#endif
+#if TCP_SNDQUEUELOWAT >= TCP_SND_QUEUELEN
+ #error "lwip_sanity_check: WARNING: TCP_SNDQUEUELOWAT must be less than TCP_SND_QUEUELEN. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error."
+#endif
+#if !MEMP_MEM_MALLOC && PBUF_POOL_SIZE && (PBUF_POOL_BUFSIZE <= (PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN))
+ #error "lwip_sanity_check: WARNING: PBUF_POOL_BUFSIZE does not provide enough space for protocol headers. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error."
+#endif
+#if !MEMP_MEM_MALLOC && PBUF_POOL_SIZE && (TCP_WND > (PBUF_POOL_SIZE * (PBUF_POOL_BUFSIZE - (PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN))))
+ #error "lwip_sanity_check: WARNING: TCP_WND is larger than space provided by PBUF_POOL_SIZE * (PBUF_POOL_BUFSIZE - protocol headers). If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error."
+#endif
+#if TCP_WND < TCP_MSS
+ #error "lwip_sanity_check: WARNING: TCP_WND is smaller than MSS. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error."
+#endif
+#endif /* LWIP_TCP */
+#endif /* !LWIP_DISABLE_TCP_SANITY_CHECKS */
+
+/**
+ * @ingroup lwip_nosys
+ * Initialize all modules.
+ * Use this in NO_SYS mode. Use tcpip_init() otherwise.
+ */
+void
+lwip_init(void)
+{
+#ifndef LWIP_SKIP_CONST_CHECK
+ int a;
+ LWIP_UNUSED_ARG(a);
+ LWIP_ASSERT("LWIP_CONST_CAST not implemented correctly. Check your lwIP port.", LWIP_CONST_CAST(void*, &a) == &a);
+#endif
+#ifndef LWIP_SKIP_PACKING_CHECK
+ LWIP_ASSERT("Struct packing not implemented correctly. Check your lwIP port.", sizeof(struct packed_struct_test) == PACKED_STRUCT_TEST_EXPECTED_SIZE);
+#endif
+
+ /* Modules initialization */
+ stats_init();
+#if !NO_SYS
+ sys_init();
+#endif /* !NO_SYS */
+ mem_init();
+ memp_init();
+ pbuf_init();
+ netif_init();
+#if LWIP_IPV4
+ ip_init();
+#if LWIP_ARP
+ etharp_init();
+#endif /* LWIP_ARP */
+#endif /* LWIP_IPV4 */
+#if LWIP_RAW
+ raw_init();
+#endif /* LWIP_RAW */
+#if LWIP_UDP
+ udp_init();
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+ tcp_init();
+#endif /* LWIP_TCP */
+#if LWIP_IGMP
+ igmp_init();
+#endif /* LWIP_IGMP */
+#if LWIP_DNS
+ dns_init();
+#endif /* LWIP_DNS */
+#if PPP_SUPPORT
+ ppp_init();
+#endif
+
+#if LWIP_TIMERS
+ sys_timeouts_init();
+#endif /* LWIP_TIMERS */
+}
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/ip.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/ip.c
new file mode 100644
index 0000000..2e02408
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/ip.c
@@ -0,0 +1,124 @@
+/**
+ * @file
+ * Common IPv4 and IPv6 code
+ *
+ * @defgroup ip IP
+ * @ingroup callbackstyle_api
+ *
+ * @defgroup ip4 IPv4
+ * @ingroup ip
+ *
+ * @defgroup ip6 IPv6
+ * @ingroup ip
+ *
+ * @defgroup ipaddr IP address handling
+ * @ingroup infrastructure
+ *
+ * @defgroup ip4addr IPv4 only
+ * @ingroup ipaddr
+ *
+ * @defgroup ip6addr IPv6 only
+ * @ingroup ipaddr
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV4 || LWIP_IPV6
+
+#include "lwip/ip_addr.h"
+#include "lwip/ip.h"
+
+/** Global data for both IPv4 and IPv6 */
+struct ip_globals ip_data;
+
+#if LWIP_IPV4 && LWIP_IPV6
+
+const ip_addr_t ip_addr_any_type = IPADDR_ANY_TYPE_INIT;
+
+/**
+ * @ingroup ipaddr
+ * Convert IP address string (both versions) to numeric.
+ * The version is auto-detected from the string.
+ *
+ * @param cp IP address string to convert
+ * @param addr conversion result is stored here
+ * @return 1 on success, 0 on error
+ */
+int
+ipaddr_aton(const char *cp, ip_addr_t *addr)
+{
+ if (cp != NULL) {
+ const char* c;
+ for (c = cp; *c != 0; c++) {
+ if (*c == ':') {
+ /* contains a colon: IPv6 address */
+ if (addr) {
+ IP_SET_TYPE_VAL(*addr, IPADDR_TYPE_V6);
+ }
+ return ip6addr_aton(cp, ip_2_ip6(addr));
+ } else if (*c == '.') {
+ /* contains a dot: IPv4 address */
+ break;
+ }
+ }
+ /* call ip4addr_aton as fallback or if IPv4 was found */
+ if (addr) {
+ IP_SET_TYPE_VAL(*addr, IPADDR_TYPE_V4);
+ }
+ return ip4addr_aton(cp, ip_2_ip4(addr));
+ }
+ return 0;
+}
+
+/**
+ * @ingroup lwip_nosys
+ * If both IP versions are enabled, this function can dispatch packets to the correct one.
+ * Don't call directly, pass to netif_add() and call netif->input().
+ */
+err_t
+ip_input(struct pbuf *p, struct netif *inp)
+{
+ if (p != NULL) {
+ if (IP_HDR_GET_VERSION(p->payload) == 6) {
+ return ip6_input(p, inp);
+ }
+ return ip4_input(p, inp);
+ }
+ return ERR_VAL;
+}
+
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+
+#endif /* LWIP_IPV4 || LWIP_IPV6 */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/ipv4/autoip.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/ipv4/autoip.c
new file mode 100644
index 0000000..10db8a3
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/ipv4/autoip.c
@@ -0,0 +1,527 @@
+/**
+ * @file
+ * AutoIP Automatic LinkLocal IP Configuration
+ *
+ * This is a AutoIP implementation for the lwIP TCP/IP stack. It aims to conform
+ * with RFC 3927.
+ *
+ * @defgroup autoip AUTOIP
+ * @ingroup ip4
+ * AUTOIP related functions
+ * USAGE:
+ *
+ * define @ref LWIP_AUTOIP 1 in your lwipopts.h
+ * Options:
+ * AUTOIP_TMR_INTERVAL msecs,
+ * I recommend a value of 100. The value must divide 1000 with a remainder almost 0.
+ * Possible values are 1000, 500, 333, 250, 200, 166, 142, 125, 111, 100 ....
+ *
+ * Without DHCP:
+ * - Call autoip_start() after netif_add().
+ *
+ * With DHCP:
+ * - define @ref LWIP_DHCP_AUTOIP_COOP 1 in your lwipopts.h.
+ * - Configure your DHCP Client.
+ *
+ * @see netifapi_autoip
+ */
+
+/*
+ *
+ * Copyright (c) 2007 Dominik Spies <kontakt@dspies.de>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Dominik Spies <kontakt@dspies.de>
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV4 && LWIP_AUTOIP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/mem.h"
+/* #include "lwip/udp.h" */
+#include "lwip/ip_addr.h"
+#include "lwip/netif.h"
+#include "lwip/autoip.h"
+#include "lwip/etharp.h"
+#include "lwip/prot/autoip.h"
+
+#include <string.h>
+
+/** Pseudo random macro based on netif informations.
+ * You could use "rand()" from the C Library if you define LWIP_AUTOIP_RAND in lwipopts.h */
+#ifndef LWIP_AUTOIP_RAND
+#define LWIP_AUTOIP_RAND(netif) ( (((u32_t)((netif->hwaddr[5]) & 0xff) << 24) | \
+ ((u32_t)((netif->hwaddr[3]) & 0xff) << 16) | \
+ ((u32_t)((netif->hwaddr[2]) & 0xff) << 8) | \
+ ((u32_t)((netif->hwaddr[4]) & 0xff))) + \
+ (netif_autoip_data(netif)? netif_autoip_data(netif)->tried_llipaddr : 0))
+#endif /* LWIP_AUTOIP_RAND */
+
+/**
+ * Macro that generates the initial IP address to be tried by AUTOIP.
+ * If you want to override this, define it to something else in lwipopts.h.
+ */
+#ifndef LWIP_AUTOIP_CREATE_SEED_ADDR
+#define LWIP_AUTOIP_CREATE_SEED_ADDR(netif) \
+ lwip_htonl(AUTOIP_RANGE_START + ((u32_t)(((u8_t)(netif->hwaddr[4])) | \
+ ((u32_t)((u8_t)(netif->hwaddr[5]))) << 8)))
+#endif /* LWIP_AUTOIP_CREATE_SEED_ADDR */
+
+/* static functions */
+static err_t autoip_arp_announce(struct netif *netif);
+static void autoip_start_probing(struct netif *netif);
+
+/**
+ * @ingroup autoip
+ * Set a statically allocated struct autoip to work with.
+ * Using this prevents autoip_start to allocate it using mem_malloc.
+ *
+ * @param netif the netif for which to set the struct autoip
+ * @param autoip (uninitialised) autoip struct allocated by the application
+ */
+void
+autoip_set_struct(struct netif *netif, struct autoip *autoip)
+{
+ LWIP_ASSERT("netif != NULL", netif != NULL);
+ LWIP_ASSERT("autoip != NULL", autoip != NULL);
+ LWIP_ASSERT("netif already has a struct autoip set",
+ netif_autoip_data(netif) == NULL);
+
+ /* clear data structure */
+ memset(autoip, 0, sizeof(struct autoip));
+ /* autoip->state = AUTOIP_STATE_OFF; */
+ netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_AUTOIP, autoip);
+}
+
+/** Restart AutoIP client and check the next address (conflict detected)
+ *
+ * @param netif The netif under AutoIP control
+ */
+static void
+autoip_restart(struct netif *netif)
+{
+ struct autoip* autoip = netif_autoip_data(netif);
+ autoip->tried_llipaddr++;
+ autoip_start(netif);
+}
+
+/**
+ * Handle a IP address conflict after an ARP conflict detection
+ */
+static void
+autoip_handle_arp_conflict(struct netif *netif)
+{
+ struct autoip* autoip = netif_autoip_data(netif);
+
+ /* RFC3927, 2.5 "Conflict Detection and Defense" allows two options where
+ a) means retreat on the first conflict and
+ b) allows to keep an already configured address when having only one
+ conflict in 10 seconds
+ We use option b) since it helps to improve the chance that one of the two
+ conflicting hosts may be able to retain its address. */
+
+ if (autoip->lastconflict > 0) {
+ /* retreat, there was a conflicting ARP in the last DEFEND_INTERVAL seconds */
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+ ("autoip_handle_arp_conflict(): we are defending, but in DEFEND_INTERVAL, retreating\n"));
+
+ /* Active TCP sessions are aborted when removing the ip addresss */
+ autoip_restart(netif);
+ } else {
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+ ("autoip_handle_arp_conflict(): we are defend, send ARP Announce\n"));
+ autoip_arp_announce(netif);
+ autoip->lastconflict = DEFEND_INTERVAL * AUTOIP_TICKS_PER_SECOND;
+ }
+}
+
+/**
+ * Create an IP-Address out of range 169.254.1.0 to 169.254.254.255
+ *
+ * @param netif network interface on which create the IP-Address
+ * @param ipaddr ip address to initialize
+ */
+static void
+autoip_create_addr(struct netif *netif, ip4_addr_t *ipaddr)
+{
+ struct autoip* autoip = netif_autoip_data(netif);
+
+ /* Here we create an IP-Address out of range 169.254.1.0 to 169.254.254.255
+ * compliant to RFC 3927 Section 2.1
+ * We have 254 * 256 possibilities */
+
+ u32_t addr = lwip_ntohl(LWIP_AUTOIP_CREATE_SEED_ADDR(netif));
+ addr += autoip->tried_llipaddr;
+ addr = AUTOIP_NET | (addr & 0xffff);
+ /* Now, 169.254.0.0 <= addr <= 169.254.255.255 */
+
+ if (addr < AUTOIP_RANGE_START) {
+ addr += AUTOIP_RANGE_END - AUTOIP_RANGE_START + 1;
+ }
+ if (addr > AUTOIP_RANGE_END) {
+ addr -= AUTOIP_RANGE_END - AUTOIP_RANGE_START + 1;
+ }
+ LWIP_ASSERT("AUTOIP address not in range", (addr >= AUTOIP_RANGE_START) &&
+ (addr <= AUTOIP_RANGE_END));
+ ip4_addr_set_u32(ipaddr, lwip_htonl(addr));
+
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+ ("autoip_create_addr(): tried_llipaddr=%"U16_F", %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ (u16_t)(autoip->tried_llipaddr), ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr),
+ ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr)));
+}
+
+/**
+ * Sends an ARP probe from a network interface
+ *
+ * @param netif network interface used to send the probe
+ */
+static err_t
+autoip_arp_probe(struct netif *netif)
+{
+ struct autoip* autoip = netif_autoip_data(netif);
+ /* this works because netif->ip_addr is ANY */
+ return etharp_request(netif, &autoip->llipaddr);
+}
+
+/**
+ * Sends an ARP announce from a network interface
+ *
+ * @param netif network interface used to send the announce
+ */
+static err_t
+autoip_arp_announce(struct netif *netif)
+{
+ return etharp_gratuitous(netif);
+}
+
+/**
+ * Configure interface for use with current LL IP-Address
+ *
+ * @param netif network interface to configure with current LL IP-Address
+ */
+static err_t
+autoip_bind(struct netif *netif)
+{
+ struct autoip* autoip = netif_autoip_data(netif);
+ ip4_addr_t sn_mask, gw_addr;
+
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE,
+ ("autoip_bind(netif=%p) %c%c%"U16_F" %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num,
+ ip4_addr1_16(&autoip->llipaddr), ip4_addr2_16(&autoip->llipaddr),
+ ip4_addr3_16(&autoip->llipaddr), ip4_addr4_16(&autoip->llipaddr)));
+
+ IP4_ADDR(&sn_mask, 255, 255, 0, 0);
+ IP4_ADDR(&gw_addr, 0, 0, 0, 0);
+
+ netif_set_addr(netif, &autoip->llipaddr, &sn_mask, &gw_addr);
+ /* interface is used by routing now that an address is set */
+
+ return ERR_OK;
+}
+
+/**
+ * @ingroup autoip
+ * Start AutoIP client
+ *
+ * @param netif network interface on which start the AutoIP client
+ */
+err_t
+autoip_start(struct netif *netif)
+{
+ struct autoip* autoip = netif_autoip_data(netif);
+ err_t result = ERR_OK;
+
+ LWIP_ERROR("netif is not up, old style port?", netif_is_up(netif), return ERR_ARG;);
+
+ /* Set IP-Address, Netmask and Gateway to 0 to make sure that
+ * ARP Packets are formed correctly
+ */
+ netif_set_addr(netif, IP4_ADDR_ANY4, IP4_ADDR_ANY4, IP4_ADDR_ANY4);
+
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+ ("autoip_start(netif=%p) %c%c%"U16_F"\n", (void*)netif, netif->name[0],
+ netif->name[1], (u16_t)netif->num));
+ if (autoip == NULL) {
+ /* no AutoIP client attached yet? */
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE,
+ ("autoip_start(): starting new AUTOIP client\n"));
+ autoip = (struct autoip *)mem_malloc(sizeof(struct autoip));
+ if (autoip == NULL) {
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE,
+ ("autoip_start(): could not allocate autoip\n"));
+ return ERR_MEM;
+ }
+ memset(autoip, 0, sizeof(struct autoip));
+ /* store this AutoIP client in the netif */
+ netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_AUTOIP, autoip);
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, ("autoip_start(): allocated autoip"));
+ } else {
+ autoip->state = AUTOIP_STATE_OFF;
+ autoip->ttw = 0;
+ autoip->sent_num = 0;
+ ip4_addr_set_zero(&autoip->llipaddr);
+ autoip->lastconflict = 0;
+ }
+
+ autoip_create_addr(netif, &(autoip->llipaddr));
+ autoip_start_probing(netif);
+
+ return result;
+}
+
+static void
+autoip_start_probing(struct netif *netif)
+{
+ struct autoip* autoip = netif_autoip_data(netif);
+
+ autoip->state = AUTOIP_STATE_PROBING;
+ autoip->sent_num = 0;
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+ ("autoip_start_probing(): changing state to PROBING: %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ ip4_addr1_16(&autoip->llipaddr), ip4_addr2_16(&autoip->llipaddr),
+ ip4_addr3_16(&autoip->llipaddr), ip4_addr4_16(&autoip->llipaddr)));
+
+ /* time to wait to first probe, this is randomly
+ * chosen out of 0 to PROBE_WAIT seconds.
+ * compliant to RFC 3927 Section 2.2.1
+ */
+ autoip->ttw = (u16_t)(LWIP_AUTOIP_RAND(netif) % (PROBE_WAIT * AUTOIP_TICKS_PER_SECOND));
+
+ /*
+ * if we tried more then MAX_CONFLICTS we must limit our rate for
+ * acquiring and probing address
+ * compliant to RFC 3927 Section 2.2.1
+ */
+ if (autoip->tried_llipaddr > MAX_CONFLICTS) {
+ autoip->ttw = RATE_LIMIT_INTERVAL * AUTOIP_TICKS_PER_SECOND;
+ }
+}
+
+/**
+ * Handle a possible change in the network configuration.
+ *
+ * If there is an AutoIP address configured, take the interface down
+ * and begin probing with the same address.
+ */
+void
+autoip_network_changed(struct netif *netif)
+{
+ struct autoip* autoip = netif_autoip_data(netif);
+
+ if (autoip && (autoip->state != AUTOIP_STATE_OFF)) {
+ autoip_start_probing(netif);
+ }
+}
+
+/**
+ * @ingroup autoip
+ * Stop AutoIP client
+ *
+ * @param netif network interface on which stop the AutoIP client
+ */
+err_t
+autoip_stop(struct netif *netif)
+{
+ struct autoip* autoip = netif_autoip_data(netif);
+
+ if (autoip != NULL) {
+ autoip->state = AUTOIP_STATE_OFF;
+ if (ip4_addr_islinklocal(netif_ip4_addr(netif))) {
+ netif_set_addr(netif, IP4_ADDR_ANY4, IP4_ADDR_ANY4, IP4_ADDR_ANY4);
+ }
+ }
+ return ERR_OK;
+}
+
+/**
+ * Has to be called in loop every AUTOIP_TMR_INTERVAL milliseconds
+ */
+void
+autoip_tmr(void)
+{
+ struct netif *netif = netif_list;
+ /* loop through netif's */
+ while (netif != NULL) {
+ struct autoip* autoip = netif_autoip_data(netif);
+ /* only act on AutoIP configured interfaces */
+ if (autoip != NULL) {
+ if (autoip->lastconflict > 0) {
+ autoip->lastconflict--;
+ }
+
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE,
+ ("autoip_tmr() AutoIP-State: %"U16_F", ttw=%"U16_F"\n",
+ (u16_t)(autoip->state), autoip->ttw));
+
+ if (autoip->ttw > 0) {
+ autoip->ttw--;
+ }
+
+ switch(autoip->state) {
+ case AUTOIP_STATE_PROBING:
+ if (autoip->ttw == 0) {
+ if (autoip->sent_num >= PROBE_NUM) {
+ /* Switch to ANNOUNCING: now we can bind to an IP address and use it */
+ autoip->state = AUTOIP_STATE_ANNOUNCING;
+ autoip_bind(netif);
+ /* autoip_bind() calls netif_set_addr(): this triggers a gratuitous ARP
+ which counts as an announcement */
+ autoip->sent_num = 1;
+ autoip->ttw = ANNOUNCE_WAIT * AUTOIP_TICKS_PER_SECOND;
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+ ("autoip_tmr(): changing state to ANNOUNCING: %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ ip4_addr1_16(&autoip->llipaddr), ip4_addr2_16(&autoip->llipaddr),
+ ip4_addr3_16(&autoip->llipaddr), ip4_addr4_16(&autoip->llipaddr)));
+ } else {
+ autoip_arp_probe(netif);
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, ("autoip_tmr() PROBING Sent Probe\n"));
+ autoip->sent_num++;
+ if (autoip->sent_num == PROBE_NUM) {
+ /* calculate time to wait to for announce */
+ autoip->ttw = ANNOUNCE_WAIT * AUTOIP_TICKS_PER_SECOND;
+ } else {
+ /* calculate time to wait to next probe */
+ autoip->ttw = (u16_t)((LWIP_AUTOIP_RAND(netif) %
+ ((PROBE_MAX - PROBE_MIN) * AUTOIP_TICKS_PER_SECOND) ) +
+ PROBE_MIN * AUTOIP_TICKS_PER_SECOND);
+ }
+ }
+ }
+ break;
+
+ case AUTOIP_STATE_ANNOUNCING:
+ if (autoip->ttw == 0) {
+ autoip_arp_announce(netif);
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, ("autoip_tmr() ANNOUNCING Sent Announce\n"));
+ autoip->ttw = ANNOUNCE_INTERVAL * AUTOIP_TICKS_PER_SECOND;
+ autoip->sent_num++;
+
+ if (autoip->sent_num >= ANNOUNCE_NUM) {
+ autoip->state = AUTOIP_STATE_BOUND;
+ autoip->sent_num = 0;
+ autoip->ttw = 0;
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+ ("autoip_tmr(): changing state to BOUND: %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ ip4_addr1_16(&autoip->llipaddr), ip4_addr2_16(&autoip->llipaddr),
+ ip4_addr3_16(&autoip->llipaddr), ip4_addr4_16(&autoip->llipaddr)));
+ }
+ }
+ break;
+
+ default:
+ /* nothing to do in other states */
+ break;
+ }
+ }
+ /* proceed to next network interface */
+ netif = netif->next;
+ }
+}
+
+/**
+ * Handles every incoming ARP Packet, called by etharp_input().
+ *
+ * @param netif network interface to use for autoip processing
+ * @param hdr Incoming ARP packet
+ */
+void
+autoip_arp_reply(struct netif *netif, struct etharp_hdr *hdr)
+{
+ struct autoip* autoip = netif_autoip_data(netif);
+
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, ("autoip_arp_reply()\n"));
+ if ((autoip != NULL) && (autoip->state != AUTOIP_STATE_OFF)) {
+ /* when ip.src == llipaddr && hw.src != netif->hwaddr
+ *
+ * when probing ip.dst == llipaddr && hw.src != netif->hwaddr
+ * we have a conflict and must solve it
+ */
+ ip4_addr_t sipaddr, dipaddr;
+ struct eth_addr netifaddr;
+ ETHADDR16_COPY(netifaddr.addr, netif->hwaddr);
+
+ /* Copy struct ip4_addr2 to aligned ip4_addr, to support compilers without
+ * structure packing (not using structure copy which breaks strict-aliasing rules).
+ */
+ IPADDR2_COPY(&sipaddr, &hdr->sipaddr);
+ IPADDR2_COPY(&dipaddr, &hdr->dipaddr);
+
+ if (autoip->state == AUTOIP_STATE_PROBING) {
+ /* RFC 3927 Section 2.2.1:
+ * from beginning to after ANNOUNCE_WAIT
+ * seconds we have a conflict if
+ * ip.src == llipaddr OR
+ * ip.dst == llipaddr && hw.src != own hwaddr
+ */
+ if ((ip4_addr_cmp(&sipaddr, &autoip->llipaddr)) ||
+ (ip4_addr_isany_val(sipaddr) &&
+ ip4_addr_cmp(&dipaddr, &autoip->llipaddr) &&
+ !eth_addr_cmp(&netifaddr, &hdr->shwaddr))) {
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | LWIP_DBG_LEVEL_WARNING,
+ ("autoip_arp_reply(): Probe Conflict detected\n"));
+ autoip_restart(netif);
+ }
+ } else {
+ /* RFC 3927 Section 2.5:
+ * in any state we have a conflict if
+ * ip.src == llipaddr && hw.src != own hwaddr
+ */
+ if (ip4_addr_cmp(&sipaddr, &autoip->llipaddr) &&
+ !eth_addr_cmp(&netifaddr, &hdr->shwaddr)) {
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | LWIP_DBG_LEVEL_WARNING,
+ ("autoip_arp_reply(): Conflicting ARP-Packet detected\n"));
+ autoip_handle_arp_conflict(netif);
+ }
+ }
+ }
+}
+
+/** check if AutoIP supplied netif->ip_addr
+ *
+ * @param netif the netif to check
+ * @return 1 if AutoIP supplied netif->ip_addr (state BOUND or ANNOUNCING),
+ * 0 otherwise
+ */
+u8_t
+autoip_supplied_address(const struct netif *netif)
+{
+ if ((netif != NULL) && (netif_autoip_data(netif) != NULL)) {
+ struct autoip* autoip = netif_autoip_data(netif);
+ return (autoip->state == AUTOIP_STATE_BOUND) || (autoip->state == AUTOIP_STATE_ANNOUNCING);
+ }
+ return 0;
+}
+
+u8_t
+autoip_accept_packet(struct netif *netif, const ip4_addr_t *addr)
+{
+ struct autoip* autoip = netif_autoip_data(netif);
+ return (autoip != NULL) && ip4_addr_cmp(addr, &(autoip->llipaddr));
+}
+
+#endif /* LWIP_IPV4 && LWIP_AUTOIP */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/ipv4/dhcp.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/ipv4/dhcp.c
new file mode 100644
index 0000000..dd35471
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/ipv4/dhcp.c
@@ -0,0 +1,1950 @@
+/**
+ * @file
+ * Dynamic Host Configuration Protocol client
+ *
+ * @defgroup dhcp4 DHCPv4
+ * @ingroup ip4
+ * DHCP (IPv4) related functions
+ * This is a DHCP client for the lwIP TCP/IP stack. It aims to conform
+ * with RFC 2131 and RFC 2132.
+ *
+ * @todo:
+ * - Support for interfaces other than Ethernet (SLIP, PPP, ...)
+ *
+ * Options:
+ * @ref DHCP_COARSE_TIMER_SECS (recommended 60 which is a minute)
+ * @ref DHCP_FINE_TIMER_MSECS (recommended 500 which equals TCP coarse timer)
+ *
+ * dhcp_start() starts a DHCP client instance which
+ * configures the interface by obtaining an IP address lease and maintaining it.
+ *
+ * Use dhcp_release() to end the lease and use dhcp_stop()
+ * to remove the DHCP client.
+ *
+ * @see netifapi_dhcp4
+ */
+
+/*
+ * Copyright (c) 2001-2004 Leon Woestenberg <leon.woestenberg@gmx.net>
+ * Copyright (c) 2001-2004 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ * The Swedish Institute of Computer Science and Adam Dunkels
+ * are specifically granted permission to redistribute this
+ * source code.
+ *
+ * Author: Leon Woestenberg <leon.woestenberg@gmx.net>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV4 && LWIP_DHCP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/stats.h"
+#include "lwip/mem.h"
+#include "lwip/udp.h"
+#include "lwip/ip_addr.h"
+#include "lwip/netif.h"
+#include "lwip/def.h"
+#include "lwip/dhcp.h"
+#include "lwip/autoip.h"
+#include "lwip/dns.h"
+#include "lwip/etharp.h"
+#include "lwip/prot/dhcp.h"
+
+#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"
+ * \#define DHCP_GLOBAL_XID rand()
+ */
+#ifdef DHCP_GLOBAL_XID_HEADER
+#include DHCP_GLOBAL_XID_HEADER /* include optional starting XID generation prototypes */
+#endif
+
+/** DHCP_OPTION_MAX_MSG_SIZE is set to the MTU
+ * MTU is checked to be big enough in dhcp_start */
+#define DHCP_MAX_MSG_LEN(netif) (netif->mtu)
+#define DHCP_MAX_MSG_LEN_MIN_REQUIRED 576
+/** Minimum length for reply before packet is parsed */
+#define DHCP_MIN_REPLY_LEN 44
+
+#define REBOOT_TRIES 2
+
+#if LWIP_DNS && LWIP_DHCP_MAX_DNS_SERVERS
+#if DNS_MAX_SERVERS > LWIP_DHCP_MAX_DNS_SERVERS
+#define LWIP_DHCP_PROVIDE_DNS_SERVERS LWIP_DHCP_MAX_DNS_SERVERS
+#else
+#define LWIP_DHCP_PROVIDE_DNS_SERVERS DNS_MAX_SERVERS
+#endif
+#else
+#define LWIP_DHCP_PROVIDE_DNS_SERVERS 0
+#endif
+
+/** Option handling: options are parsed in dhcp_parse_reply
+ * and saved in an array where other functions can load them from.
+ * This might be moved into the struct dhcp (not necessarily since
+ * lwIP is single-threaded and the array is only used while in recv
+ * callback). */
+enum dhcp_option_idx {
+ DHCP_OPTION_IDX_OVERLOAD = 0,
+ DHCP_OPTION_IDX_MSG_TYPE,
+ DHCP_OPTION_IDX_SERVER_ID,
+ DHCP_OPTION_IDX_LEASE_TIME,
+ DHCP_OPTION_IDX_T1,
+ DHCP_OPTION_IDX_T2,
+ DHCP_OPTION_IDX_SUBNET_MASK,
+ DHCP_OPTION_IDX_ROUTER,
+#if LWIP_DHCP_PROVIDE_DNS_SERVERS
+ DHCP_OPTION_IDX_DNS_SERVER,
+ DHCP_OPTION_IDX_DNS_SERVER_LAST = DHCP_OPTION_IDX_DNS_SERVER + LWIP_DHCP_PROVIDE_DNS_SERVERS - 1,
+#endif /* LWIP_DHCP_PROVIDE_DNS_SERVERS */
+#if LWIP_DHCP_GET_NTP_SRV
+ DHCP_OPTION_IDX_NTP_SERVER,
+ DHCP_OPTION_IDX_NTP_SERVER_LAST = DHCP_OPTION_IDX_NTP_SERVER + LWIP_DHCP_MAX_NTP_SERVERS - 1,
+#endif /* LWIP_DHCP_GET_NTP_SRV */
+ DHCP_OPTION_IDX_MAX
+};
+
+/** Holds the decoded option values, only valid while in dhcp_recv.
+ @todo: move this into struct dhcp? */
+u32_t dhcp_rx_options_val[DHCP_OPTION_IDX_MAX];
+/** Holds a flag which option was received and is contained in dhcp_rx_options_val,
+ only valid while in dhcp_recv.
+ @todo: move this into struct dhcp? */
+u8_t dhcp_rx_options_given[DHCP_OPTION_IDX_MAX];
+
+static u8_t dhcp_discover_request_options[] = {
+ DHCP_OPTION_SUBNET_MASK,
+ DHCP_OPTION_ROUTER,
+ DHCP_OPTION_BROADCAST
+#if LWIP_DHCP_PROVIDE_DNS_SERVERS
+ , DHCP_OPTION_DNS_SERVER
+#endif /* LWIP_DHCP_PROVIDE_DNS_SERVERS */
+#if LWIP_DHCP_GET_NTP_SRV
+ , DHCP_OPTION_NTP
+#endif /* LWIP_DHCP_GET_NTP_SRV */
+ };
+
+#ifdef DHCP_GLOBAL_XID
+static u32_t xid;
+static u8_t xid_initialised;
+#endif /* DHCP_GLOBAL_XID */
+
+#define dhcp_option_given(dhcp, idx) (dhcp_rx_options_given[idx] != 0)
+#define dhcp_got_option(dhcp, idx) (dhcp_rx_options_given[idx] = 1)
+#define dhcp_clear_option(dhcp, idx) (dhcp_rx_options_given[idx] = 0)
+#define dhcp_clear_all_options(dhcp) (memset(dhcp_rx_options_given, 0, sizeof(dhcp_rx_options_given)))
+#define dhcp_get_option_value(dhcp, idx) (dhcp_rx_options_val[idx])
+#define dhcp_set_option_value(dhcp, idx, val) (dhcp_rx_options_val[idx] = (val))
+
+static struct udp_pcb *dhcp_pcb;
+static u8_t dhcp_pcb_refcount;
+
+/* DHCP client state machine functions */
+static err_t dhcp_discover(struct netif *netif);
+static err_t dhcp_select(struct netif *netif);
+static void dhcp_bind(struct netif *netif);
+#if DHCP_DOES_ARP_CHECK
+static err_t dhcp_decline(struct netif *netif);
+#endif /* DHCP_DOES_ARP_CHECK */
+static err_t dhcp_rebind(struct netif *netif);
+static err_t dhcp_reboot(struct netif *netif);
+static void dhcp_set_state(struct dhcp *dhcp, u8_t new_state);
+
+/* receive, unfold, parse and free incoming messages */
+static void dhcp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port);
+
+/* set the DHCP timers */
+static void dhcp_timeout(struct netif *netif);
+static void dhcp_t1_timeout(struct netif *netif);
+static void dhcp_t2_timeout(struct netif *netif);
+
+/* build outgoing messages */
+/* create a DHCP message, fill in common headers */
+static err_t dhcp_create_msg(struct netif *netif, struct dhcp *dhcp, u8_t message_type);
+/* free a DHCP request */
+static void dhcp_delete_msg(struct dhcp *dhcp);
+/* add a DHCP option (type, then length in bytes) */
+static void dhcp_option(struct dhcp *dhcp, u8_t option_type, u8_t option_len);
+/* add option values */
+static void dhcp_option_byte(struct dhcp *dhcp, u8_t value);
+static void dhcp_option_short(struct dhcp *dhcp, u16_t value);
+static void dhcp_option_long(struct dhcp *dhcp, u32_t value);
+#if LWIP_NETIF_HOSTNAME
+static void dhcp_option_hostname(struct dhcp *dhcp, struct netif *netif);
+#endif /* LWIP_NETIF_HOSTNAME */
+/* always add the DHCP options trailer to end and pad */
+static void dhcp_option_trailer(struct dhcp *dhcp);
+
+/** Ensure DHCP PCB is allocated and bound */
+static err_t
+dhcp_inc_pcb_refcount(void)
+{
+ if (dhcp_pcb_refcount == 0) {
+ LWIP_ASSERT("dhcp_inc_pcb_refcount(): memory leak", dhcp_pcb == NULL);
+
+ /* allocate UDP PCB */
+ dhcp_pcb = udp_new();
+
+ if (dhcp_pcb == NULL) {
+ return ERR_MEM;
+ }
+
+ ip_set_option(dhcp_pcb, SOF_BROADCAST);
+
+ /* set up local and remote port for the pcb -> listen on all interfaces on all src/dest IPs */
+ udp_bind(dhcp_pcb, IP4_ADDR_ANY, DHCP_CLIENT_PORT);
+ udp_connect(dhcp_pcb, IP4_ADDR_ANY, DHCP_SERVER_PORT);
+ udp_recv(dhcp_pcb, dhcp_recv, NULL);
+ }
+
+ dhcp_pcb_refcount++;
+
+ return ERR_OK;
+}
+
+/** Free DHCP PCB if the last netif stops using it */
+static void
+dhcp_dec_pcb_refcount(void)
+{
+ LWIP_ASSERT("dhcp_pcb_refcount(): refcount error", (dhcp_pcb_refcount > 0));
+ dhcp_pcb_refcount--;
+
+ if (dhcp_pcb_refcount == 0) {
+ udp_remove(dhcp_pcb);
+ dhcp_pcb = NULL;
+ }
+}
+
+/**
+ * Back-off the DHCP client (because of a received NAK response).
+ *
+ * Back-off the DHCP client because of a received NAK. Receiving a
+ * NAK means the client asked for something non-sensible, for
+ * example when it tries to renew a lease obtained on another network.
+ *
+ * We clear any existing set IP address and restart DHCP negotiation
+ * afresh (as per RFC2131 3.2.3).
+ *
+ * @param netif the netif under DHCP control
+ */
+static void
+dhcp_handle_nak(struct netif *netif)
+{
+ struct dhcp *dhcp = netif_dhcp_data(netif);
+
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_handle_nak(netif=%p) %c%c%"U16_F"\n",
+ (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num));
+ /* Change to a defined state - set this before assigning the address
+ to ensure the callback can use dhcp_supplied_address() */
+ dhcp_set_state(dhcp, DHCP_STATE_BACKING_OFF);
+ /* remove IP address from interface (must no longer be used, as per RFC2131) */
+ netif_set_addr(netif, IP4_ADDR_ANY4, IP4_ADDR_ANY4, IP4_ADDR_ANY4);
+ /* We can immediately restart discovery */
+ dhcp_discover(netif);
+}
+
+#if DHCP_DOES_ARP_CHECK
+/**
+ * Checks if the offered IP address is already in use.
+ *
+ * It does so by sending an ARP request for the offered address and
+ * entering CHECKING state. If no ARP reply is received within a small
+ * interval, the address is assumed to be free for use by us.
+ *
+ * @param netif the netif under DHCP control
+ */
+static void
+dhcp_check(struct netif *netif)
+{
+ struct dhcp *dhcp = netif_dhcp_data(netif);
+ err_t result;
+ u16_t msecs;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_check(netif=%p) %c%c\n", (void *)netif, (s16_t)netif->name[0],
+ (s16_t)netif->name[1]));
+ dhcp_set_state(dhcp, DHCP_STATE_CHECKING);
+ /* create an ARP query for the offered IP address, expecting that no host
+ responds, as the IP address should not be in use. */
+ result = etharp_query(netif, &dhcp->offered_ip_addr, NULL);
+ if (result != ERR_OK) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("dhcp_check: could not perform ARP query\n"));
+ }
+ if (dhcp->tries < 255) {
+ dhcp->tries++;
+ }
+ msecs = 500;
+ dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_check(): set request timeout %"U16_F" msecs\n", msecs));
+}
+#endif /* DHCP_DOES_ARP_CHECK */
+
+/**
+ * Remember the configuration offered by a DHCP server.
+ *
+ * @param netif the netif under DHCP control
+ */
+static void
+dhcp_handle_offer(struct netif *netif)
+{
+ struct dhcp *dhcp = netif_dhcp_data(netif);
+
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_handle_offer(netif=%p) %c%c%"U16_F"\n",
+ (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num));
+ /* obtain the server address */
+ if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_SERVER_ID)) {
+ ip_addr_set_ip4_u32(&dhcp->server_ip_addr, lwip_htonl(dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_SERVER_ID)));
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_handle_offer(): server 0x%08"X32_F"\n",
+ ip4_addr_get_u32(ip_2_ip4(&dhcp->server_ip_addr))));
+ /* remember offered address */
+ ip4_addr_copy(dhcp->offered_ip_addr, dhcp->msg_in->yiaddr);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_handle_offer(): offer for 0x%08"X32_F"\n",
+ ip4_addr_get_u32(&dhcp->offered_ip_addr)));
+
+ dhcp_select(netif);
+ } else {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
+ ("dhcp_handle_offer(netif=%p) did not get server ID!\n", (void*)netif));
+ }
+}
+
+/**
+ * Select a DHCP server offer out of all offers.
+ *
+ * Simply select the first offer received.
+ *
+ * @param netif the netif under DHCP control
+ * @return lwIP specific error (see error.h)
+ */
+static err_t
+dhcp_select(struct netif *netif)
+{
+ struct dhcp *dhcp = netif_dhcp_data(netif);
+ err_t result;
+ u16_t msecs;
+ u8_t i;
+
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_select(netif=%p) %c%c%"U16_F"\n", (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num));
+ dhcp_set_state(dhcp, DHCP_STATE_REQUESTING);
+
+ /* create and initialize the DHCP message header */
+ result = dhcp_create_msg(netif, dhcp, DHCP_REQUEST);
+ if (result == ERR_OK) {
+ dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
+ dhcp_option_short(dhcp, DHCP_MAX_MSG_LEN(netif));
+
+ /* MUST request the offered IP address */
+ dhcp_option(dhcp, DHCP_OPTION_REQUESTED_IP, 4);
+ dhcp_option_long(dhcp, lwip_ntohl(ip4_addr_get_u32(&dhcp->offered_ip_addr)));
+
+ dhcp_option(dhcp, DHCP_OPTION_SERVER_ID, 4);
+ dhcp_option_long(dhcp, lwip_ntohl(ip4_addr_get_u32(ip_2_ip4(&dhcp->server_ip_addr))));
+
+ dhcp_option(dhcp, DHCP_OPTION_PARAMETER_REQUEST_LIST, LWIP_ARRAYSIZE(dhcp_discover_request_options));
+ for (i = 0; i < LWIP_ARRAYSIZE(dhcp_discover_request_options); i++) {
+ dhcp_option_byte(dhcp, dhcp_discover_request_options[i]);
+ }
+
+#if LWIP_NETIF_HOSTNAME
+ dhcp_option_hostname(dhcp, netif);
+#endif /* LWIP_NETIF_HOSTNAME */
+
+ dhcp_option_trailer(dhcp);
+ /* shrink the pbuf to the actual content length */
+ pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len);
+
+ /* send broadcast to any DHCP server */
+ udp_sendto_if_src(dhcp_pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif, IP4_ADDR_ANY);
+ dhcp_delete_msg(dhcp);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_select: REQUESTING\n"));
+ } else {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("dhcp_select: could not allocate DHCP request\n"));
+ }
+ if (dhcp->tries < 255) {
+ dhcp->tries++;
+ }
+ msecs = (dhcp->tries < 6 ? 1 << dhcp->tries : 60) * 1000;
+ dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_select(): set request timeout %"U16_F" msecs\n", msecs));
+ return result;
+}
+
+/**
+ * The DHCP timer that checks for lease renewal/rebind timeouts.
+ * Must be called once a minute (see @ref DHCP_COARSE_TIMER_SECS).
+ */
+void
+dhcp_coarse_tmr(void)
+{
+ struct netif *netif = netif_list;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_coarse_tmr()\n"));
+ /* iterate through all network interfaces */
+ while (netif != NULL) {
+ /* only act on DHCP configured interfaces */
+ struct dhcp *dhcp = netif_dhcp_data(netif);
+ if ((dhcp != NULL) && (dhcp->state != DHCP_STATE_OFF)) {
+ /* compare lease time to expire timeout */
+ if (dhcp->t0_timeout && (++dhcp->lease_used == dhcp->t0_timeout)) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_coarse_tmr(): t0 timeout\n"));
+ /* this clients' lease time has expired */
+ dhcp_release(netif);
+ dhcp_discover(netif);
+ /* timer is active (non zero), and triggers (zeroes) now? */
+ } else if (dhcp->t2_rebind_time && (dhcp->t2_rebind_time-- == 1)) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_coarse_tmr(): t2 timeout\n"));
+ /* this clients' rebind timeout triggered */
+ dhcp_t2_timeout(netif);
+ /* timer is active (non zero), and triggers (zeroes) now */
+ } else if (dhcp->t1_renew_time && (dhcp->t1_renew_time-- == 1)) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_coarse_tmr(): t1 timeout\n"));
+ /* this clients' renewal timeout triggered */
+ dhcp_t1_timeout(netif);
+ }
+ }
+ /* proceed to next netif */
+ netif = netif->next;
+ }
+}
+
+/**
+ * DHCP transaction timeout handling (this function must be called every 500ms,
+ * see @ref DHCP_FINE_TIMER_MSECS).
+ *
+ * A DHCP server is expected to respond within a short period of time.
+ * This timer checks whether an outstanding DHCP request is timed out.
+ */
+void
+dhcp_fine_tmr(void)
+{
+ struct netif *netif = netif_list;
+ /* loop through netif's */
+ while (netif != NULL) {
+ struct dhcp *dhcp = netif_dhcp_data(netif);
+ /* only act on DHCP configured interfaces */
+ if (dhcp != NULL) {
+ /* timer is active (non zero), and is about to trigger now */
+ if (dhcp->request_timeout > 1) {
+ dhcp->request_timeout--;
+ }
+ else if (dhcp->request_timeout == 1) {
+ dhcp->request_timeout--;
+ /* { netif->dhcp->request_timeout == 0 } */
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_fine_tmr(): request timeout\n"));
+ /* this client's request timeout triggered */
+ dhcp_timeout(netif);
+ }
+ }
+ /* proceed to next network interface */
+ netif = netif->next;
+ }
+}
+
+/**
+ * A DHCP negotiation transaction, or ARP request, has timed out.
+ *
+ * The timer that was started with the DHCP or ARP request has
+ * timed out, indicating no response was received in time.
+ *
+ * @param netif the netif under DHCP control
+ */
+static void
+dhcp_timeout(struct netif *netif)
+{
+ struct dhcp *dhcp = netif_dhcp_data(netif);
+
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_timeout()\n"));
+ /* back-off period has passed, or server selection timed out */
+ if ((dhcp->state == DHCP_STATE_BACKING_OFF) || (dhcp->state == DHCP_STATE_SELECTING)) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_timeout(): restarting discovery\n"));
+ dhcp_discover(netif);
+ /* receiving the requested lease timed out */
+ } else if (dhcp->state == DHCP_STATE_REQUESTING) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): REQUESTING, DHCP request timed out\n"));
+ if (dhcp->tries <= 5) {
+ dhcp_select(netif);
+ } else {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): REQUESTING, releasing, restarting\n"));
+ dhcp_release(netif);
+ dhcp_discover(netif);
+ }
+#if DHCP_DOES_ARP_CHECK
+ /* received no ARP reply for the offered address (which is good) */
+ } else if (dhcp->state == DHCP_STATE_CHECKING) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): CHECKING, ARP request timed out\n"));
+ if (dhcp->tries <= 1) {
+ dhcp_check(netif);
+ /* no ARP replies on the offered address,
+ looks like the IP address is indeed free */
+ } else {
+ /* bind the interface to the offered address */
+ dhcp_bind(netif);
+ }
+#endif /* DHCP_DOES_ARP_CHECK */
+ } else if (dhcp->state == DHCP_STATE_REBOOTING) {
+ if (dhcp->tries < REBOOT_TRIES) {
+ dhcp_reboot(netif);
+ } else {
+ dhcp_discover(netif);
+ }
+ }
+}
+
+/**
+ * The renewal period has timed out.
+ *
+ * @param netif the netif under DHCP control
+ */
+static void
+dhcp_t1_timeout(struct netif *netif)
+{
+ struct dhcp *dhcp = netif_dhcp_data(netif);
+
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_t1_timeout()\n"));
+ if ((dhcp->state == DHCP_STATE_REQUESTING) || (dhcp->state == DHCP_STATE_BOUND) ||
+ (dhcp->state == DHCP_STATE_RENEWING)) {
+ /* just retry to renew - note that the rebind timer (t2) will
+ * eventually time-out if renew tries fail. */
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+ ("dhcp_t1_timeout(): must renew\n"));
+ /* This slightly different to RFC2131: DHCPREQUEST will be sent from state
+ DHCP_STATE_RENEWING, not DHCP_STATE_BOUND */
+ dhcp_renew(netif);
+ /* Calculate next timeout */
+ if (((dhcp->t2_timeout - dhcp->lease_used) / 2) >= ((60 + DHCP_COARSE_TIMER_SECS / 2) / DHCP_COARSE_TIMER_SECS))
+ {
+ dhcp->t1_renew_time = ((dhcp->t2_timeout - dhcp->lease_used) / 2);
+ }
+ }
+}
+
+/**
+ * The rebind period has timed out.
+ *
+ * @param netif the netif under DHCP control
+ */
+static void
+dhcp_t2_timeout(struct netif *netif)
+{
+ struct dhcp *dhcp = netif_dhcp_data(netif);
+
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_t2_timeout()\n"));
+ if ((dhcp->state == DHCP_STATE_REQUESTING) || (dhcp->state == DHCP_STATE_BOUND) ||
+ (dhcp->state == DHCP_STATE_RENEWING) || (dhcp->state == DHCP_STATE_REBINDING)) {
+ /* just retry to rebind */
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+ ("dhcp_t2_timeout(): must rebind\n"));
+ /* This slightly different to RFC2131: DHCPREQUEST will be sent from state
+ DHCP_STATE_REBINDING, not DHCP_STATE_BOUND */
+ dhcp_rebind(netif);
+ /* Calculate next timeout */
+ if (((dhcp->t0_timeout - dhcp->lease_used) / 2) >= ((60 + DHCP_COARSE_TIMER_SECS / 2) / DHCP_COARSE_TIMER_SECS))
+ {
+ dhcp->t2_rebind_time = ((dhcp->t0_timeout - dhcp->lease_used) / 2);
+ }
+ }
+}
+
+/**
+ * Handle a DHCP ACK packet
+ *
+ * @param netif the netif under DHCP control
+ */
+static void
+dhcp_handle_ack(struct netif *netif)
+{
+ struct dhcp *dhcp = netif_dhcp_data(netif);
+
+#if LWIP_DHCP_PROVIDE_DNS_SERVERS || LWIP_DHCP_GET_NTP_SRV
+ u8_t n;
+#endif /* LWIP_DHCP_PROVIDE_DNS_SERVERS || LWIP_DHCP_GET_NTP_SRV */
+#if LWIP_DHCP_GET_NTP_SRV
+ ip4_addr_t ntp_server_addrs[LWIP_DHCP_MAX_NTP_SERVERS];
+#endif
+
+ /* clear options we might not get from the ACK */
+ ip4_addr_set_zero(&dhcp->offered_sn_mask);
+ ip4_addr_set_zero(&dhcp->offered_gw_addr);
+#if LWIP_DHCP_BOOTP_FILE
+ ip4_addr_set_zero(&dhcp->offered_si_addr);
+#endif /* LWIP_DHCP_BOOTP_FILE */
+
+ /* lease time given? */
+ if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_LEASE_TIME)) {
+ /* remember offered lease time */
+ dhcp->offered_t0_lease = dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_LEASE_TIME);
+ }
+ /* renewal period given? */
+ if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_T1)) {
+ /* remember given renewal period */
+ dhcp->offered_t1_renew = dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_T1);
+ } else {
+ /* calculate safe periods for renewal */
+ dhcp->offered_t1_renew = dhcp->offered_t0_lease / 2;
+ }
+
+ /* renewal period given? */
+ if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_T2)) {
+ /* remember given rebind period */
+ dhcp->offered_t2_rebind = dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_T2);
+ } else {
+ /* calculate safe periods for rebinding (offered_t0_lease * 0.875 -> 87.5%)*/
+ dhcp->offered_t2_rebind = (dhcp->offered_t0_lease * 7U) / 8U;
+ }
+
+ /* (y)our internet address */
+ ip4_addr_copy(dhcp->offered_ip_addr, dhcp->msg_in->yiaddr);
+
+#if LWIP_DHCP_BOOTP_FILE
+ /* copy boot server address,
+ boot file name copied in dhcp_parse_reply if not overloaded */
+ ip4_addr_copy(dhcp->offered_si_addr, dhcp->msg_in->siaddr);
+#endif /* LWIP_DHCP_BOOTP_FILE */
+
+ /* subnet mask given? */
+ if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_SUBNET_MASK)) {
+ /* remember given subnet mask */
+ ip4_addr_set_u32(&dhcp->offered_sn_mask, lwip_htonl(dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_SUBNET_MASK)));
+ dhcp->subnet_mask_given = 1;
+ } else {
+ dhcp->subnet_mask_given = 0;
+ }
+
+ /* gateway router */
+ if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_ROUTER)) {
+ ip4_addr_set_u32(&dhcp->offered_gw_addr, lwip_htonl(dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_ROUTER)));
+ }
+
+#if LWIP_DHCP_GET_NTP_SRV
+ /* NTP servers */
+ for (n = 0; (n < LWIP_DHCP_MAX_NTP_SERVERS) && dhcp_option_given(dhcp, DHCP_OPTION_IDX_NTP_SERVER + n); n++) {
+ ip4_addr_set_u32(&ntp_server_addrs[n], lwip_htonl(dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_NTP_SERVER + n)));
+ }
+ dhcp_set_ntp_servers(n, ntp_server_addrs);
+#endif /* LWIP_DHCP_GET_NTP_SRV */
+
+#if LWIP_DHCP_PROVIDE_DNS_SERVERS
+ /* DNS servers */
+ for (n = 0; (n < LWIP_DHCP_PROVIDE_DNS_SERVERS) && dhcp_option_given(dhcp, DHCP_OPTION_IDX_DNS_SERVER + n); n++) {
+ ip_addr_t dns_addr;
+ ip_addr_set_ip4_u32(&dns_addr, lwip_htonl(dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_DNS_SERVER + n)));
+ dns_setserver(n, &dns_addr);
+ }
+#endif /* LWIP_DHCP_PROVIDE_DNS_SERVERS */
+}
+
+/**
+ * @ingroup dhcp4
+ * Set a statically allocated struct dhcp to work with.
+ * Using this prevents dhcp_start to allocate it using mem_malloc.
+ *
+ * @param netif the netif for which to set the struct dhcp
+ * @param dhcp (uninitialised) dhcp struct allocated by the application
+ */
+void
+dhcp_set_struct(struct netif *netif, struct dhcp *dhcp)
+{
+ LWIP_ASSERT("netif != NULL", netif != NULL);
+ LWIP_ASSERT("dhcp != NULL", dhcp != NULL);
+ LWIP_ASSERT("netif already has a struct dhcp set", netif_dhcp_data(netif) == NULL);
+
+ /* clear data structure */
+ memset(dhcp, 0, sizeof(struct dhcp));
+ /* dhcp_set_state(&dhcp, DHCP_STATE_OFF); */
+ netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP, dhcp);
+}
+
+/**
+ * @ingroup dhcp4
+ * Removes a struct dhcp from a netif.
+ *
+ * ATTENTION: Only use this when not using dhcp_set_struct() to allocate the
+ * struct dhcp since the memory is passed back to the heap.
+ *
+ * @param netif the netif from which to remove the struct dhcp
+ */
+void dhcp_cleanup(struct netif *netif)
+{
+ LWIP_ASSERT("netif != NULL", netif != NULL);
+
+ if (netif_dhcp_data(netif) != NULL) {
+ mem_free(netif_dhcp_data(netif));
+ netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP, NULL);
+ }
+}
+
+/**
+ * @ingroup dhcp4
+ * Start DHCP negotiation for a network interface.
+ *
+ * If no DHCP client instance was attached to this interface,
+ * a new client is created first. If a DHCP client instance
+ * was already present, it restarts negotiation.
+ *
+ * @param netif The lwIP network interface
+ * @return lwIP error code
+ * - ERR_OK - No error
+ * - ERR_MEM - Out of memory
+ */
+err_t
+dhcp_start(struct netif *netif)
+{
+ struct dhcp *dhcp;
+ err_t result;
+
+ LWIP_ERROR("netif != NULL", (netif != NULL), return ERR_ARG;);
+ LWIP_ERROR("netif is not up, old style port?", netif_is_up(netif), return ERR_ARG;);
+ dhcp = netif_dhcp_data(netif);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_start(netif=%p) %c%c%"U16_F"\n", (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num));
+
+ /* check MTU of the netif */
+ if (netif->mtu < DHCP_MAX_MSG_LEN_MIN_REQUIRED) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): Cannot use this netif with DHCP: MTU is too small\n"));
+ return ERR_MEM;
+ }
+
+ /* no DHCP client attached yet? */
+ if (dhcp == NULL) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): starting new DHCP client\n"));
+ dhcp = (struct dhcp *)mem_malloc(sizeof(struct dhcp));
+ if (dhcp == NULL) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): could not allocate dhcp\n"));
+ return ERR_MEM;
+ }
+
+ /* store this dhcp client in the netif */
+ netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP, dhcp);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): allocated dhcp"));
+ /* already has DHCP client attached */
+ } else {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_start(): restarting DHCP configuration\n"));
+ LWIP_ASSERT("pbuf p_out wasn't freed", dhcp->p_out == NULL);
+ LWIP_ASSERT("reply wasn't freed", dhcp->msg_in == NULL );
+
+ if (dhcp->pcb_allocated != 0) {
+ dhcp_dec_pcb_refcount(); /* free DHCP PCB if not needed any more */
+ }
+ /* dhcp is cleared below, no need to reset flag*/
+ }
+
+ /* clear data structure */
+ memset(dhcp, 0, sizeof(struct dhcp));
+ /* dhcp_set_state(&dhcp, DHCP_STATE_OFF); */
+
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): starting DHCP configuration\n"));
+
+ if (dhcp_inc_pcb_refcount() != ERR_OK) { /* ensure DHCP PCB is allocated */
+ return ERR_MEM;
+ }
+ dhcp->pcb_allocated = 1;
+
+#if LWIP_DHCP_CHECK_LINK_UP
+ if (!netif_is_link_up(netif)) {
+ /* set state INIT and wait for dhcp_network_changed() to call dhcp_discover() */
+ dhcp_set_state(dhcp, DHCP_STATE_INIT);
+ return ERR_OK;
+ }
+#endif /* LWIP_DHCP_CHECK_LINK_UP */
+
+
+ /* (re)start the DHCP negotiation */
+ result = dhcp_discover(netif);
+ if (result != ERR_OK) {
+ /* free resources allocated above */
+ dhcp_stop(netif);
+ return ERR_MEM;
+ }
+ return result;
+}
+
+/**
+ * @ingroup dhcp4
+ * Inform a DHCP server of our manual configuration.
+ *
+ * This informs DHCP servers of our fixed IP address configuration
+ * by sending an INFORM message. It does not involve DHCP address
+ * configuration, it is just here to be nice to the network.
+ *
+ * @param netif The lwIP network interface
+ */
+void
+dhcp_inform(struct netif *netif)
+{
+ struct dhcp dhcp;
+ err_t result = ERR_OK;
+
+ LWIP_ERROR("netif != NULL", (netif != NULL), return;);
+
+ if (dhcp_inc_pcb_refcount() != ERR_OK) { /* ensure DHCP PCB is allocated */
+ return;
+ }
+
+ memset(&dhcp, 0, sizeof(struct dhcp));
+ dhcp_set_state(&dhcp, DHCP_STATE_INFORMING);
+
+ /* create and initialize the DHCP message header */
+ result = dhcp_create_msg(netif, &dhcp, DHCP_INFORM);
+ if (result == ERR_OK) {
+ dhcp_option(&dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
+ dhcp_option_short(&dhcp, DHCP_MAX_MSG_LEN(netif));
+
+ dhcp_option_trailer(&dhcp);
+
+ pbuf_realloc(dhcp.p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp.options_out_len);
+
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_inform: INFORMING\n"));
+
+ udp_sendto_if(dhcp_pcb, dhcp.p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif);
+
+ dhcp_delete_msg(&dhcp);
+ } else {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_inform: could not allocate DHCP request\n"));
+ }
+
+ dhcp_dec_pcb_refcount(); /* delete DHCP PCB if not needed any more */
+}
+
+/** Handle a possible change in the network configuration.
+ *
+ * This enters the REBOOTING state to verify that the currently bound
+ * address is still valid.
+ */
+void
+dhcp_network_changed(struct netif *netif)
+{
+ struct dhcp *dhcp = netif_dhcp_data(netif);
+
+ if (!dhcp)
+ return;
+ switch (dhcp->state) {
+ case DHCP_STATE_REBINDING:
+ case DHCP_STATE_RENEWING:
+ case DHCP_STATE_BOUND:
+ case DHCP_STATE_REBOOTING:
+ dhcp->tries = 0;
+ dhcp_reboot(netif);
+ break;
+ case DHCP_STATE_OFF:
+ /* stay off */
+ break;
+ default:
+ /* INIT/REQUESTING/CHECKING/BACKING_OFF restart with new 'rid' because the
+ state changes, SELECTING: continue with current 'rid' as we stay in the
+ same state */
+#if LWIP_DHCP_AUTOIP_COOP
+ if (dhcp->autoip_coop_state == DHCP_AUTOIP_COOP_STATE_ON) {
+ autoip_stop(netif);
+ dhcp->autoip_coop_state = DHCP_AUTOIP_COOP_STATE_OFF;
+ }
+#endif /* LWIP_DHCP_AUTOIP_COOP */
+ /* ensure we start with short timeouts, even if already discovering */
+ dhcp->tries = 0;
+ dhcp_discover(netif);
+ break;
+ }
+}
+
+#if DHCP_DOES_ARP_CHECK
+/**
+ * Match an ARP reply with the offered IP address:
+ * check whether the offered IP address is not in use using ARP
+ *
+ * @param netif the network interface on which the reply was received
+ * @param addr The IP address we received a reply from
+ */
+void
+dhcp_arp_reply(struct netif *netif, const ip4_addr_t *addr)
+{
+ struct dhcp *dhcp;
+
+ LWIP_ERROR("netif != NULL", (netif != NULL), return;);
+ dhcp = netif_dhcp_data(netif);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_arp_reply()\n"));
+ /* is a DHCP client doing an ARP check? */
+ if ((dhcp != NULL) && (dhcp->state == DHCP_STATE_CHECKING)) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_arp_reply(): CHECKING, arp reply for 0x%08"X32_F"\n",
+ ip4_addr_get_u32(addr)));
+ /* did a host respond with the address we
+ were offered by the DHCP server? */
+ if (ip4_addr_cmp(addr, &dhcp->offered_ip_addr)) {
+ /* we will not accept the offered address */
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | LWIP_DBG_LEVEL_WARNING,
+ ("dhcp_arp_reply(): arp reply matched with offered address, declining\n"));
+ dhcp_decline(netif);
+ }
+ }
+}
+
+/**
+ * Decline an offered lease.
+ *
+ * Tell the DHCP server we do not accept the offered address.
+ * One reason to decline the lease is when we find out the address
+ * is already in use by another host (through ARP).
+ *
+ * @param netif the netif under DHCP control
+ */
+static err_t
+dhcp_decline(struct netif *netif)
+{
+ struct dhcp *dhcp = netif_dhcp_data(netif);
+ err_t result = ERR_OK;
+ u16_t msecs;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_decline()\n"));
+ dhcp_set_state(dhcp, DHCP_STATE_BACKING_OFF);
+ /* create and initialize the DHCP message header */
+ result = dhcp_create_msg(netif, dhcp, DHCP_DECLINE);
+ if (result == ERR_OK) {
+ dhcp_option(dhcp, DHCP_OPTION_REQUESTED_IP, 4);
+ dhcp_option_long(dhcp, lwip_ntohl(ip4_addr_get_u32(&dhcp->offered_ip_addr)));
+
+ dhcp_option_trailer(dhcp);
+ /* resize pbuf to reflect true size of options */
+ pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len);
+
+ /* per section 4.4.4, broadcast DECLINE messages */
+ udp_sendto_if_src(dhcp_pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif, IP4_ADDR_ANY);
+ dhcp_delete_msg(dhcp);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_decline: BACKING OFF\n"));
+ } else {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
+ ("dhcp_decline: could not allocate DHCP request\n"));
+ }
+ if (dhcp->tries < 255) {
+ dhcp->tries++;
+ }
+ msecs = 10*1000;
+ dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_decline(): set request timeout %"U16_F" msecs\n", msecs));
+ return result;
+}
+#endif /* DHCP_DOES_ARP_CHECK */
+
+
+/**
+ * Start the DHCP process, discover a DHCP server.
+ *
+ * @param netif the netif under DHCP control
+ */
+static err_t
+dhcp_discover(struct netif *netif)
+{
+ struct dhcp *dhcp = netif_dhcp_data(netif);
+ err_t result = ERR_OK;
+ u16_t msecs;
+ u8_t i;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover()\n"));
+ ip4_addr_set_any(&dhcp->offered_ip_addr);
+ dhcp_set_state(dhcp, DHCP_STATE_SELECTING);
+ /* create and initialize the DHCP message header */
+ result = dhcp_create_msg(netif, dhcp, DHCP_DISCOVER);
+ if (result == ERR_OK) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover: making request\n"));
+
+ dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
+ dhcp_option_short(dhcp, DHCP_MAX_MSG_LEN(netif));
+
+ dhcp_option(dhcp, DHCP_OPTION_PARAMETER_REQUEST_LIST, LWIP_ARRAYSIZE(dhcp_discover_request_options));
+ for (i = 0; i < LWIP_ARRAYSIZE(dhcp_discover_request_options); i++) {
+ dhcp_option_byte(dhcp, dhcp_discover_request_options[i]);
+ }
+ dhcp_option_trailer(dhcp);
+
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover: realloc()ing\n"));
+ pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len);
+
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover: sendto(DISCOVER, IP_ADDR_BROADCAST, DHCP_SERVER_PORT)\n"));
+ udp_sendto_if_src(dhcp_pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif, IP4_ADDR_ANY);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover: deleting()ing\n"));
+ dhcp_delete_msg(dhcp);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_discover: SELECTING\n"));
+ } else {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_discover: could not allocate DHCP request\n"));
+ }
+ if (dhcp->tries < 255) {
+ dhcp->tries++;
+ }
+#if LWIP_DHCP_AUTOIP_COOP
+ if (dhcp->tries >= LWIP_DHCP_AUTOIP_COOP_TRIES && dhcp->autoip_coop_state == DHCP_AUTOIP_COOP_STATE_OFF) {
+ dhcp->autoip_coop_state = DHCP_AUTOIP_COOP_STATE_ON;
+ autoip_start(netif);
+ }
+#endif /* LWIP_DHCP_AUTOIP_COOP */
+ msecs = (dhcp->tries < 6 ? 1 << dhcp->tries : 60) * 1000;
+ dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_discover(): set request timeout %"U16_F" msecs\n", msecs));
+ return result;
+}
+
+
+/**
+ * Bind the interface to the offered IP address.
+ *
+ * @param netif network interface to bind to the offered address
+ */
+static void
+dhcp_bind(struct netif *netif)
+{
+ u32_t timeout;
+ struct dhcp *dhcp;
+ ip4_addr_t sn_mask, gw_addr;
+ LWIP_ERROR("dhcp_bind: netif != NULL", (netif != NULL), return;);
+ dhcp = netif_dhcp_data(netif);
+ LWIP_ERROR("dhcp_bind: dhcp != NULL", (dhcp != NULL), return;);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_bind(netif=%p) %c%c%"U16_F"\n", (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num));
+
+ /* reset time used of lease */
+ dhcp->lease_used = 0;
+
+ if (dhcp->offered_t0_lease != 0xffffffffUL) {
+ /* set renewal period timer */
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_bind(): t0 renewal timer %"U32_F" secs\n", dhcp->offered_t0_lease));
+ timeout = (dhcp->offered_t0_lease + DHCP_COARSE_TIMER_SECS / 2) / DHCP_COARSE_TIMER_SECS;
+ if (timeout > 0xffff) {
+ timeout = 0xffff;
+ }
+ dhcp->t0_timeout = (u16_t)timeout;
+ if (dhcp->t0_timeout == 0) {
+ dhcp->t0_timeout = 1;
+ }
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_bind(): set request timeout %"U32_F" msecs\n", dhcp->offered_t0_lease*1000));
+ }
+
+ /* temporary DHCP lease? */
+ if (dhcp->offered_t1_renew != 0xffffffffUL) {
+ /* set renewal period timer */
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_bind(): t1 renewal timer %"U32_F" secs\n", dhcp->offered_t1_renew));
+ timeout = (dhcp->offered_t1_renew + DHCP_COARSE_TIMER_SECS / 2) / DHCP_COARSE_TIMER_SECS;
+ if (timeout > 0xffff) {
+ timeout = 0xffff;
+ }
+ dhcp->t1_timeout = (u16_t)timeout;
+ if (dhcp->t1_timeout == 0) {
+ dhcp->t1_timeout = 1;
+ }
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_bind(): set request timeout %"U32_F" msecs\n", dhcp->offered_t1_renew*1000));
+ dhcp->t1_renew_time = dhcp->t1_timeout;
+ }
+ /* set renewal period timer */
+ if (dhcp->offered_t2_rebind != 0xffffffffUL) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_bind(): t2 rebind timer %"U32_F" secs\n", dhcp->offered_t2_rebind));
+ timeout = (dhcp->offered_t2_rebind + DHCP_COARSE_TIMER_SECS / 2) / DHCP_COARSE_TIMER_SECS;
+ if (timeout > 0xffff) {
+ timeout = 0xffff;
+ }
+ dhcp->t2_timeout = (u16_t)timeout;
+ if (dhcp->t2_timeout == 0) {
+ dhcp->t2_timeout = 1;
+ }
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_bind(): set request timeout %"U32_F" msecs\n", dhcp->offered_t2_rebind*1000));
+ dhcp->t2_rebind_time = dhcp->t2_timeout;
+ }
+
+ /* 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 */
+ ip4_addr_copy(sn_mask, dhcp->offered_sn_mask);
+ } else {
+ /* subnet mask not given, choose a safe subnet mask given the network class */
+ u8_t first_octet = ip4_addr1(&dhcp->offered_ip_addr);
+ if (first_octet <= 127) {
+ ip4_addr_set_u32(&sn_mask, PP_HTONL(0xff000000UL));
+ } else if (first_octet >= 192) {
+ ip4_addr_set_u32(&sn_mask, PP_HTONL(0xffffff00UL));
+ } else {
+ ip4_addr_set_u32(&sn_mask, PP_HTONL(0xffff0000UL));
+ }
+ }
+
+ ip4_addr_copy(gw_addr, dhcp->offered_gw_addr);
+ /* gateway address not given? */
+ if (ip4_addr_isany_val(gw_addr)) {
+ /* copy network address */
+ ip4_addr_get_network(&gw_addr, &dhcp->offered_ip_addr, &sn_mask);
+ /* use first host address on network as gateway */
+ ip4_addr_set_u32(&gw_addr, ip4_addr_get_u32(&gw_addr) | PP_HTONL(0x00000001UL));
+ }
+
+#if LWIP_DHCP_AUTOIP_COOP
+ if (dhcp->autoip_coop_state == DHCP_AUTOIP_COOP_STATE_ON) {
+ autoip_stop(netif);
+ dhcp->autoip_coop_state = DHCP_AUTOIP_COOP_STATE_OFF;
+ }
+#endif /* LWIP_DHCP_AUTOIP_COOP */
+
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_bind(): IP: 0x%08"X32_F" SN: 0x%08"X32_F" GW: 0x%08"X32_F"\n",
+ ip4_addr_get_u32(&dhcp->offered_ip_addr), ip4_addr_get_u32(&sn_mask), ip4_addr_get_u32(&gw_addr)));
+ /* netif is now bound to DHCP leased address - set this before assigning the address
+ to ensure the callback can use dhcp_supplied_address() */
+ dhcp_set_state(dhcp, DHCP_STATE_BOUND);
+
+ netif_set_addr(netif, &dhcp->offered_ip_addr, &sn_mask, &gw_addr);
+ /* interface is used by routing now that an address is set */
+}
+
+/**
+ * @ingroup dhcp4
+ * Renew an existing DHCP lease at the involved DHCP server.
+ *
+ * @param netif network interface which must renew its lease
+ */
+err_t
+dhcp_renew(struct netif *netif)
+{
+ struct dhcp *dhcp = netif_dhcp_data(netif);
+ err_t result;
+ u16_t msecs;
+ u8_t i;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_renew()\n"));
+ dhcp_set_state(dhcp, DHCP_STATE_RENEWING);
+
+ /* create and initialize the DHCP message header */
+ result = dhcp_create_msg(netif, dhcp, DHCP_REQUEST);
+ if (result == ERR_OK) {
+ dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
+ dhcp_option_short(dhcp, DHCP_MAX_MSG_LEN(netif));
+
+ dhcp_option(dhcp, DHCP_OPTION_PARAMETER_REQUEST_LIST, LWIP_ARRAYSIZE(dhcp_discover_request_options));
+ for (i = 0; i < LWIP_ARRAYSIZE(dhcp_discover_request_options); i++) {
+ dhcp_option_byte(dhcp, dhcp_discover_request_options[i]);
+ }
+
+#if LWIP_NETIF_HOSTNAME
+ dhcp_option_hostname(dhcp, netif);
+#endif /* LWIP_NETIF_HOSTNAME */
+
+ /* append DHCP message trailer */
+ dhcp_option_trailer(dhcp);
+
+ pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len);
+
+ udp_sendto_if(dhcp_pcb, dhcp->p_out, &dhcp->server_ip_addr, DHCP_SERVER_PORT, netif);
+ dhcp_delete_msg(dhcp);
+
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_renew: RENEWING\n"));
+ } else {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_renew: could not allocate DHCP request\n"));
+ }
+ if (dhcp->tries < 255) {
+ dhcp->tries++;
+ }
+ /* back-off on retries, but to a maximum of 20 seconds */
+ msecs = dhcp->tries < 10 ? dhcp->tries * 2000 : 20 * 1000;
+ dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_renew(): set request timeout %"U16_F" msecs\n", msecs));
+ return result;
+}
+
+/**
+ * Rebind with a DHCP server for an existing DHCP lease.
+ *
+ * @param netif network interface which must rebind with a DHCP server
+ */
+static err_t
+dhcp_rebind(struct netif *netif)
+{
+ struct dhcp *dhcp = netif_dhcp_data(netif);
+ err_t result;
+ u16_t msecs;
+ u8_t i;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_rebind()\n"));
+ dhcp_set_state(dhcp, DHCP_STATE_REBINDING);
+
+ /* create and initialize the DHCP message header */
+ result = dhcp_create_msg(netif, dhcp, DHCP_REQUEST);
+ if (result == ERR_OK) {
+ dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
+ dhcp_option_short(dhcp, DHCP_MAX_MSG_LEN(netif));
+
+ dhcp_option(dhcp, DHCP_OPTION_PARAMETER_REQUEST_LIST, LWIP_ARRAYSIZE(dhcp_discover_request_options));
+ for (i = 0; i < LWIP_ARRAYSIZE(dhcp_discover_request_options); i++) {
+ dhcp_option_byte(dhcp, dhcp_discover_request_options[i]);
+ }
+
+#if LWIP_NETIF_HOSTNAME
+ dhcp_option_hostname(dhcp, netif);
+#endif /* LWIP_NETIF_HOSTNAME */
+
+ dhcp_option_trailer(dhcp);
+
+ pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len);
+
+ /* broadcast to server */
+ udp_sendto_if(dhcp_pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif);
+ dhcp_delete_msg(dhcp);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_rebind: REBINDING\n"));
+ } else {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_rebind: could not allocate DHCP request\n"));
+ }
+ if (dhcp->tries < 255) {
+ dhcp->tries++;
+ }
+ msecs = dhcp->tries < 10 ? dhcp->tries * 1000 : 10 * 1000;
+ dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_rebind(): set request timeout %"U16_F" msecs\n", msecs));
+ return result;
+}
+
+/**
+ * Enter REBOOTING state to verify an existing lease
+ *
+ * @param netif network interface which must reboot
+ */
+static err_t
+dhcp_reboot(struct netif *netif)
+{
+ struct dhcp *dhcp = netif_dhcp_data(netif);
+ err_t result;
+ u16_t msecs;
+ u8_t i;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_reboot()\n"));
+ dhcp_set_state(dhcp, DHCP_STATE_REBOOTING);
+
+ /* create and initialize the DHCP message header */
+ result = dhcp_create_msg(netif, dhcp, DHCP_REQUEST);
+ if (result == ERR_OK) {
+ dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
+ dhcp_option_short(dhcp, DHCP_MAX_MSG_LEN_MIN_REQUIRED);
+
+ dhcp_option(dhcp, DHCP_OPTION_REQUESTED_IP, 4);
+ dhcp_option_long(dhcp, lwip_ntohl(ip4_addr_get_u32(&dhcp->offered_ip_addr)));
+
+ dhcp_option(dhcp, DHCP_OPTION_PARAMETER_REQUEST_LIST, LWIP_ARRAYSIZE(dhcp_discover_request_options));
+ for (i = 0; i < LWIP_ARRAYSIZE(dhcp_discover_request_options); i++) {
+ dhcp_option_byte(dhcp, dhcp_discover_request_options[i]);
+ }
+
+ dhcp_option_trailer(dhcp);
+
+ pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len);
+
+ /* broadcast to server */
+ udp_sendto_if(dhcp_pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif);
+ dhcp_delete_msg(dhcp);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_reboot: REBOOTING\n"));
+ } else {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_reboot: could not allocate DHCP request\n"));
+ }
+ if (dhcp->tries < 255) {
+ dhcp->tries++;
+ }
+ msecs = dhcp->tries < 10 ? dhcp->tries * 1000 : 10 * 1000;
+ dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_reboot(): set request timeout %"U16_F" msecs\n", msecs));
+ return result;
+}
+
+
+/**
+ * @ingroup dhcp4
+ * Release a DHCP lease (usually called before @ref dhcp_stop).
+ *
+ * @param netif network interface which must release its lease
+ */
+err_t
+dhcp_release(struct netif *netif)
+{
+ struct dhcp *dhcp = netif_dhcp_data(netif);
+ err_t result;
+ ip_addr_t server_ip_addr;
+ u8_t is_dhcp_supplied_address;
+
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_release()\n"));
+ if (dhcp == NULL) {
+ return ERR_ARG;
+ }
+ ip_addr_copy(server_ip_addr, dhcp->server_ip_addr);
+
+ is_dhcp_supplied_address = dhcp_supplied_address(netif);
+
+ /* idle DHCP client */
+ dhcp_set_state(dhcp, DHCP_STATE_OFF);
+ /* clean old DHCP offer */
+ ip_addr_set_zero_ip4(&dhcp->server_ip_addr);
+ ip4_addr_set_zero(&dhcp->offered_ip_addr);
+ ip4_addr_set_zero(&dhcp->offered_sn_mask);
+ ip4_addr_set_zero(&dhcp->offered_gw_addr);
+#if LWIP_DHCP_BOOTP_FILE
+ ip4_addr_set_zero(&dhcp->offered_si_addr);
+#endif /* LWIP_DHCP_BOOTP_FILE */
+ dhcp->offered_t0_lease = dhcp->offered_t1_renew = dhcp->offered_t2_rebind = 0;
+ dhcp->t1_renew_time = dhcp->t2_rebind_time = dhcp->lease_used = dhcp->t0_timeout = 0;
+
+ if (!is_dhcp_supplied_address) {
+ /* don't issue release message when address is not dhcp-assigned */
+ return ERR_OK;
+ }
+
+ /* create and initialize the DHCP message header */
+ result = dhcp_create_msg(netif, dhcp, DHCP_RELEASE);
+ if (result == ERR_OK) {
+ dhcp_option(dhcp, DHCP_OPTION_SERVER_ID, 4);
+ dhcp_option_long(dhcp, lwip_ntohl(ip4_addr_get_u32(ip_2_ip4(&server_ip_addr))));
+
+ dhcp_option_trailer(dhcp);
+
+ pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len);
+
+ udp_sendto_if(dhcp_pcb, dhcp->p_out, &server_ip_addr, DHCP_SERVER_PORT, netif);
+ dhcp_delete_msg(dhcp);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_release: RELEASED, DHCP_STATE_OFF\n"));
+ } else {
+ /* sending release failed, but that's not a problem since the correct behaviour of dhcp does not rely on release */
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_release: could not allocate DHCP request\n"));
+ }
+ /* remove IP address from interface (prevents routing from selecting this interface) */
+ netif_set_addr(netif, IP4_ADDR_ANY4, IP4_ADDR_ANY4, IP4_ADDR_ANY4);
+
+ return result;
+}
+
+/**
+ * @ingroup dhcp4
+ * Remove the DHCP client from the interface.
+ *
+ * @param netif The network interface to stop DHCP on
+ */
+void
+dhcp_stop(struct netif *netif)
+{
+ struct dhcp *dhcp;
+ LWIP_ERROR("dhcp_stop: netif != NULL", (netif != NULL), return;);
+ dhcp = netif_dhcp_data(netif);
+
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_stop()\n"));
+ /* netif is DHCP configured? */
+ if (dhcp != NULL) {
+#if LWIP_DHCP_AUTOIP_COOP
+ if (dhcp->autoip_coop_state == DHCP_AUTOIP_COOP_STATE_ON) {
+ autoip_stop(netif);
+ dhcp->autoip_coop_state = DHCP_AUTOIP_COOP_STATE_OFF;
+ }
+#endif /* LWIP_DHCP_AUTOIP_COOP */
+
+ LWIP_ASSERT("reply wasn't freed", dhcp->msg_in == NULL);
+ dhcp_set_state(dhcp, DHCP_STATE_OFF);
+
+ if (dhcp->pcb_allocated != 0) {
+ dhcp_dec_pcb_refcount(); /* free DHCP PCB if not needed any more */
+ dhcp->pcb_allocated = 0;
+ }
+ }
+}
+
+/*
+ * Set the DHCP state of a DHCP client.
+ *
+ * If the state changed, reset the number of tries.
+ */
+static void
+dhcp_set_state(struct dhcp *dhcp, u8_t new_state)
+{
+ if (new_state != dhcp->state) {
+ dhcp->state = new_state;
+ dhcp->tries = 0;
+ dhcp->request_timeout = 0;
+ }
+}
+
+/*
+ * Concatenate an option type and length field to the outgoing
+ * DHCP message.
+ *
+ */
+static void
+dhcp_option(struct dhcp *dhcp, u8_t option_type, u8_t option_len)
+{
+ LWIP_ASSERT("dhcp_option: dhcp->options_out_len + 2 + option_len <= DHCP_OPTIONS_LEN", dhcp->options_out_len + 2U + option_len <= DHCP_OPTIONS_LEN);
+ dhcp->msg_out->options[dhcp->options_out_len++] = option_type;
+ dhcp->msg_out->options[dhcp->options_out_len++] = option_len;
+}
+/*
+ * Concatenate a single byte to the outgoing DHCP message.
+ *
+ */
+static void
+dhcp_option_byte(struct dhcp *dhcp, u8_t value)
+{
+ LWIP_ASSERT("dhcp_option_byte: dhcp->options_out_len < DHCP_OPTIONS_LEN", dhcp->options_out_len < DHCP_OPTIONS_LEN);
+ dhcp->msg_out->options[dhcp->options_out_len++] = value;
+}
+
+static void
+dhcp_option_short(struct dhcp *dhcp, u16_t value)
+{
+ LWIP_ASSERT("dhcp_option_short: dhcp->options_out_len + 2 <= DHCP_OPTIONS_LEN", dhcp->options_out_len + 2U <= DHCP_OPTIONS_LEN);
+ dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t)((value & 0xff00U) >> 8);
+ dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t) (value & 0x00ffU);
+}
+
+static void
+dhcp_option_long(struct dhcp *dhcp, u32_t value)
+{
+ LWIP_ASSERT("dhcp_option_long: dhcp->options_out_len + 4 <= DHCP_OPTIONS_LEN", dhcp->options_out_len + 4U <= DHCP_OPTIONS_LEN);
+ dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t)((value & 0xff000000UL) >> 24);
+ dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t)((value & 0x00ff0000UL) >> 16);
+ dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t)((value & 0x0000ff00UL) >> 8);
+ dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t)((value & 0x000000ffUL));
+}
+
+#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) {
+ size_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);
+ LWIP_ASSERT("DHCP: hostname is too long!", len <= 0xFF);
+ dhcp_option(dhcp, DHCP_OPTION_HOSTNAME, (u8_t)len);
+ while (len--) {
+ dhcp_option_byte(dhcp, *p++);
+ }
+ }
+ }
+}
+#endif /* LWIP_NETIF_HOSTNAME */
+
+/**
+ * Extract the DHCP message and the DHCP options.
+ *
+ * Extract the DHCP message and the DHCP options, each into a contiguous
+ * piece of memory. As a DHCP message is variable sized by its options,
+ * and also allows overriding some fields for options, the easy approach
+ * is to first unfold the options into a contiguous piece of memory, and
+ * use that further on.
+ *
+ */
+static err_t
+dhcp_parse_reply(struct dhcp *dhcp, struct pbuf *p)
+{
+ u8_t *options;
+ u16_t offset;
+ u16_t offset_max;
+ u16_t options_idx;
+ u16_t options_idx_max;
+ struct pbuf *q;
+ int parse_file_as_options = 0;
+ int parse_sname_as_options = 0;
+
+ /* clear received options */
+ dhcp_clear_all_options(dhcp);
+ /* check that beginning of dhcp_msg (up to and including chaddr) is in first pbuf */
+ if (p->len < DHCP_SNAME_OFS) {
+ return ERR_BUF;
+ }
+ dhcp->msg_in = (struct dhcp_msg *)p->payload;
+#if LWIP_DHCP_BOOTP_FILE
+ /* clear boot file name */
+ dhcp->boot_file_name[0] = 0;
+#endif /* LWIP_DHCP_BOOTP_FILE */
+
+ /* parse options */
+
+ /* start with options field */
+ options_idx = DHCP_OPTIONS_OFS;
+ /* parse options to the end of the received packet */
+ options_idx_max = p->tot_len;
+again:
+ q = p;
+ while ((q != NULL) && (options_idx >= q->len)) {
+ options_idx -= q->len;
+ options_idx_max -= q->len;
+ q = q->next;
+ }
+ if (q == NULL) {
+ return ERR_BUF;
+ }
+ offset = options_idx;
+ offset_max = options_idx_max;
+ options = (u8_t*)q->payload;
+ /* at least 1 byte to read and no end marker, then at least 3 bytes to read? */
+ while ((q != NULL) && (options[offset] != DHCP_OPTION_END) && (offset < offset_max)) {
+ u8_t op = options[offset];
+ u8_t len;
+ u8_t decode_len = 0;
+ int decode_idx = -1;
+ u16_t val_offset = offset + 2;
+ /* len byte might be in the next pbuf */
+ if ((offset + 1) < q->len) {
+ len = options[offset + 1];
+ } else {
+ len = (q->next != NULL ? ((u8_t*)q->next->payload)[0] : 0);
+ }
+ /* LWIP_DEBUGF(DHCP_DEBUG, ("msg_offset=%"U16_F", q->len=%"U16_F, msg_offset, q->len)); */
+ decode_len = len;
+ switch(op) {
+ /* case(DHCP_OPTION_END): handled above */
+ case(DHCP_OPTION_PAD):
+ /* special option: no len encoded */
+ decode_len = len = 0;
+ /* will be increased below */
+ offset--;
+ break;
+ case(DHCP_OPTION_SUBNET_MASK):
+ LWIP_ERROR("len == 4", len == 4, return ERR_VAL;);
+ decode_idx = DHCP_OPTION_IDX_SUBNET_MASK;
+ break;
+ case(DHCP_OPTION_ROUTER):
+ decode_len = 4; /* only copy the first given router */
+ LWIP_ERROR("len >= decode_len", len >= decode_len, return ERR_VAL;);
+ decode_idx = DHCP_OPTION_IDX_ROUTER;
+ break;
+#if LWIP_DHCP_PROVIDE_DNS_SERVERS
+ case(DHCP_OPTION_DNS_SERVER):
+ /* special case: there might be more than one server */
+ LWIP_ERROR("len %% 4 == 0", len % 4 == 0, return ERR_VAL;);
+ /* limit number of DNS servers */
+ decode_len = LWIP_MIN(len, 4 * DNS_MAX_SERVERS);
+ LWIP_ERROR("len >= decode_len", len >= decode_len, return ERR_VAL;);
+ decode_idx = DHCP_OPTION_IDX_DNS_SERVER;
+ break;
+#endif /* LWIP_DHCP_PROVIDE_DNS_SERVERS */
+ case(DHCP_OPTION_LEASE_TIME):
+ LWIP_ERROR("len == 4", len == 4, return ERR_VAL;);
+ decode_idx = DHCP_OPTION_IDX_LEASE_TIME;
+ break;
+#if LWIP_DHCP_GET_NTP_SRV
+ case(DHCP_OPTION_NTP):
+ /* special case: there might be more than one server */
+ LWIP_ERROR("len %% 4 == 0", len % 4 == 0, return ERR_VAL;);
+ /* limit number of NTP servers */
+ decode_len = LWIP_MIN(len, 4 * LWIP_DHCP_MAX_NTP_SERVERS);
+ LWIP_ERROR("len >= decode_len", len >= decode_len, return ERR_VAL;);
+ decode_idx = DHCP_OPTION_IDX_NTP_SERVER;
+ break;
+#endif /* LWIP_DHCP_GET_NTP_SRV*/
+ case(DHCP_OPTION_OVERLOAD):
+ LWIP_ERROR("len == 1", len == 1, return ERR_VAL;);
+ /* decode overload only in options, not in file/sname: invalid packet */
+ LWIP_ERROR("overload in file/sname", options_idx == DHCP_OPTIONS_OFS, return ERR_VAL;);
+ decode_idx = DHCP_OPTION_IDX_OVERLOAD;
+ break;
+ case(DHCP_OPTION_MESSAGE_TYPE):
+ LWIP_ERROR("len == 1", len == 1, return ERR_VAL;);
+ decode_idx = DHCP_OPTION_IDX_MSG_TYPE;
+ break;
+ case(DHCP_OPTION_SERVER_ID):
+ LWIP_ERROR("len == 4", len == 4, return ERR_VAL;);
+ decode_idx = DHCP_OPTION_IDX_SERVER_ID;
+ break;
+ case(DHCP_OPTION_T1):
+ LWIP_ERROR("len == 4", len == 4, return ERR_VAL;);
+ decode_idx = DHCP_OPTION_IDX_T1;
+ break;
+ case(DHCP_OPTION_T2):
+ LWIP_ERROR("len == 4", len == 4, return ERR_VAL;);
+ decode_idx = DHCP_OPTION_IDX_T2;
+ break;
+ default:
+ decode_len = 0;
+ LWIP_DEBUGF(DHCP_DEBUG, ("skipping option %"U16_F" in options\n", (u16_t)op));
+ break;
+ }
+ offset += len + 2;
+ if (decode_len > 0) {
+ u32_t value = 0;
+ u16_t copy_len;
+decode_next:
+ LWIP_ASSERT("check decode_idx", decode_idx >= 0 && decode_idx < DHCP_OPTION_IDX_MAX);
+ if (!dhcp_option_given(dhcp, decode_idx)) {
+ copy_len = LWIP_MIN(decode_len, 4);
+ if (pbuf_copy_partial(q, &value, copy_len, val_offset) != copy_len) {
+ return ERR_BUF;
+ }
+ 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, lwip_htonl(value));
+ decode_len -= 4;
+ val_offset += 4;
+ decode_idx++;
+ goto decode_next;
+ } else if (decode_len == 4) {
+ value = lwip_ntohl(value);
+ } else {
+ LWIP_ERROR("invalid decode_len", decode_len == 1, return ERR_VAL;);
+ value = ((u8_t*)&value)[0];
+ }
+ dhcp_got_option(dhcp, decode_idx);
+ dhcp_set_option_value(dhcp, decode_idx, value);
+ }
+ }
+ if (offset >= q->len) {
+ offset -= q->len;
+ offset_max -= q->len;
+ if ((offset < offset_max) && offset_max) {
+ q = q->next;
+ LWIP_ASSERT("next pbuf was null", q);
+ options = (u8_t*)q->payload;
+ } else {
+ /* We've run out of bytes, probably no end marker. Don't proceed. */
+ break;
+ }
+ }
+ }
+ /* is this an overloaded message? */
+ if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_OVERLOAD)) {
+ u32_t overload = dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_OVERLOAD);
+ dhcp_clear_option(dhcp, DHCP_OPTION_IDX_OVERLOAD);
+ if (overload == DHCP_OVERLOAD_FILE) {
+ parse_file_as_options = 1;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("overloaded file field\n"));
+ } else if (overload == DHCP_OVERLOAD_SNAME) {
+ parse_sname_as_options = 1;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("overloaded sname field\n"));
+ } else if (overload == DHCP_OVERLOAD_SNAME_FILE) {
+ parse_sname_as_options = 1;
+ parse_file_as_options = 1;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("overloaded sname and file field\n"));
+ } else {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("invalid overload option: %d\n", (int)overload));
+ }
+#if LWIP_DHCP_BOOTP_FILE
+ if (!parse_file_as_options) {
+ /* only do this for ACK messages */
+ if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_MSG_TYPE) &&
+ (dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_MSG_TYPE) == DHCP_ACK))
+ /* copy bootp file name, don't care for sname (server hostname) */
+ if (pbuf_copy_partial(p, dhcp->boot_file_name, DHCP_FILE_LEN-1, DHCP_FILE_OFS) != (DHCP_FILE_LEN-1)) {
+ return ERR_BUF;
+ }
+ /* make sure the string is really NULL-terminated */
+ dhcp->boot_file_name[DHCP_FILE_LEN-1] = 0;
+ }
+#endif /* LWIP_DHCP_BOOTP_FILE */
+ }
+ if (parse_file_as_options) {
+ /* if both are overloaded, parse file first and then sname (RFC 2131 ch. 4.1) */
+ parse_file_as_options = 0;
+ options_idx = DHCP_FILE_OFS;
+ options_idx_max = DHCP_FILE_OFS + DHCP_FILE_LEN;
+ goto again;
+ } else if (parse_sname_as_options) {
+ parse_sname_as_options = 0;
+ options_idx = DHCP_SNAME_OFS;
+ options_idx_max = DHCP_SNAME_OFS + DHCP_SNAME_LEN;
+ goto again;
+ }
+ return ERR_OK;
+}
+
+/**
+ * If an incoming DHCP message is in response to us, then trigger the state machine
+ */
+static void
+dhcp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
+{
+ struct netif *netif = ip_current_input_netif();
+ struct dhcp *dhcp = netif_dhcp_data(netif);
+ struct dhcp_msg *reply_msg = (struct dhcp_msg *)p->payload;
+ u8_t msg_type;
+ u8_t i;
+
+ LWIP_UNUSED_ARG(arg);
+
+ /* Caught DHCP message from netif that does not have DHCP enabled? -> not interested */
+ if ((dhcp == NULL) || (dhcp->pcb_allocated == 0)) {
+ goto free_pbuf_and_return;
+ }
+
+ LWIP_ASSERT("invalid server address type", IP_IS_V4(addr));
+
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_recv(pbuf = %p) from DHCP server %"U16_F".%"U16_F".%"U16_F".%"U16_F" port %"U16_F"\n", (void*)p,
+ ip4_addr1_16(ip_2_ip4(addr)), ip4_addr2_16(ip_2_ip4(addr)), ip4_addr3_16(ip_2_ip4(addr)), ip4_addr4_16(ip_2_ip4(addr)), port));
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("pbuf->len = %"U16_F"\n", p->len));
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("pbuf->tot_len = %"U16_F"\n", p->tot_len));
+ /* prevent warnings about unused arguments */
+ LWIP_UNUSED_ARG(pcb);
+ LWIP_UNUSED_ARG(addr);
+ LWIP_UNUSED_ARG(port);
+
+ LWIP_ASSERT("reply wasn't freed", dhcp->msg_in == NULL);
+
+ if (p->len < DHCP_MIN_REPLY_LEN) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("DHCP reply message or pbuf too short\n"));
+ goto free_pbuf_and_return;
+ }
+
+ if (reply_msg->op != DHCP_BOOTREPLY) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("not a DHCP reply message, but type %"U16_F"\n", (u16_t)reply_msg->op));
+ goto free_pbuf_and_return;
+ }
+ /* iterate through hardware address and match against DHCP message */
+ for (i = 0; i < netif->hwaddr_len && i < NETIF_MAX_HWADDR_LEN && i < DHCP_CHADDR_LEN; i++) {
+ if (netif->hwaddr[i] != reply_msg->chaddr[i]) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING,
+ ("netif->hwaddr[%"U16_F"]==%02"X16_F" != reply_msg->chaddr[%"U16_F"]==%02"X16_F"\n",
+ (u16_t)i, (u16_t)netif->hwaddr[i], (u16_t)i, (u16_t)reply_msg->chaddr[i]));
+ goto free_pbuf_and_return;
+ }
+ }
+ /* match transaction ID against what we expected */
+ if (lwip_ntohl(reply_msg->xid) != dhcp->xid) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING,
+ ("transaction id mismatch reply_msg->xid(%"X32_F")!=dhcp->xid(%"X32_F")\n",lwip_ntohl(reply_msg->xid),dhcp->xid));
+ goto free_pbuf_and_return;
+ }
+ /* option fields could be unfold? */
+ if (dhcp_parse_reply(dhcp, p) != ERR_OK) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
+ ("problem unfolding DHCP message - too short on memory?\n"));
+ goto free_pbuf_and_return;
+ }
+
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("searching DHCP_OPTION_MESSAGE_TYPE\n"));
+ /* obtain pointer to DHCP message type */
+ if (!dhcp_option_given(dhcp, DHCP_OPTION_IDX_MSG_TYPE)) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("DHCP_OPTION_MESSAGE_TYPE option not found\n"));
+ goto free_pbuf_and_return;
+ }
+
+ /* read DHCP message type */
+ msg_type = (u8_t)dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_MSG_TYPE);
+ /* message type is DHCP ACK? */
+ if (msg_type == DHCP_ACK) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("DHCP_ACK received\n"));
+ /* in requesting state? */
+ if (dhcp->state == DHCP_STATE_REQUESTING) {
+ dhcp_handle_ack(netif);
+#if DHCP_DOES_ARP_CHECK
+ if ((netif->flags & NETIF_FLAG_ETHARP) != 0) {
+ /* check if the acknowledged lease address is already in use */
+ dhcp_check(netif);
+ } else {
+ /* bind interface to the acknowledged lease address */
+ dhcp_bind(netif);
+ }
+#else
+ /* bind interface to the acknowledged lease address */
+ dhcp_bind(netif);
+#endif
+ }
+ /* already bound to the given lease address? */
+ else if ((dhcp->state == DHCP_STATE_REBOOTING) || (dhcp->state == DHCP_STATE_REBINDING) ||
+ (dhcp->state == DHCP_STATE_RENEWING)) {
+ dhcp_handle_ack(netif);
+ dhcp_bind(netif);
+ }
+ }
+ /* received a DHCP_NAK in appropriate state? */
+ else if ((msg_type == DHCP_NAK) &&
+ ((dhcp->state == DHCP_STATE_REBOOTING) || (dhcp->state == DHCP_STATE_REQUESTING) ||
+ (dhcp->state == DHCP_STATE_REBINDING) || (dhcp->state == DHCP_STATE_RENEWING ))) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("DHCP_NAK received\n"));
+ dhcp_handle_nak(netif);
+ }
+ /* received a DHCP_OFFER in DHCP_STATE_SELECTING state? */
+ else if ((msg_type == DHCP_OFFER) && (dhcp->state == DHCP_STATE_SELECTING)) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("DHCP_OFFER received in DHCP_STATE_SELECTING state\n"));
+ dhcp->request_timeout = 0;
+ /* remember offered lease */
+ dhcp_handle_offer(netif);
+ }
+
+free_pbuf_and_return:
+ if (dhcp != NULL) {
+ dhcp->msg_in = NULL;
+ }
+ pbuf_free(p);
+}
+
+/**
+ * Create a DHCP request, fill in common headers
+ *
+ * @param netif the netif under DHCP control
+ * @param dhcp dhcp control struct
+ * @param message_type message type of the request
+ */
+static err_t
+dhcp_create_msg(struct netif *netif, struct dhcp *dhcp, u8_t message_type)
+{
+ u16_t i;
+#ifndef DHCP_GLOBAL_XID
+ /** default global transaction identifier starting value (easy to match
+ * with a packet analyser). We simply increment for each new request.
+ * Predefine DHCP_GLOBAL_XID to a better value or a function call to generate one
+ * at runtime, any supporting function prototypes can be defined in DHCP_GLOBAL_XID_HEADER */
+#if DHCP_CREATE_RAND_XID && defined(LWIP_RAND)
+ static u32_t xid;
+#else /* DHCP_CREATE_RAND_XID && defined(LWIP_RAND) */
+ static u32_t xid = 0xABCD0000;
+#endif /* DHCP_CREATE_RAND_XID && defined(LWIP_RAND) */
+#else
+ if (!xid_initialised) {
+ xid = DHCP_GLOBAL_XID;
+ xid_initialised = !xid_initialised;
+ }
+#endif
+ LWIP_ERROR("dhcp_create_msg: netif != NULL", (netif != NULL), return ERR_ARG;);
+ LWIP_ERROR("dhcp_create_msg: dhcp != NULL", (dhcp != NULL), return ERR_VAL;);
+ LWIP_ASSERT("dhcp_create_msg: dhcp->p_out == NULL", dhcp->p_out == NULL);
+ LWIP_ASSERT("dhcp_create_msg: dhcp->msg_out == NULL", dhcp->msg_out == NULL);
+ dhcp->p_out = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct dhcp_msg), PBUF_RAM);
+ if (dhcp->p_out == NULL) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
+ ("dhcp_create_msg(): could not allocate pbuf\n"));
+ return ERR_MEM;
+ }
+ LWIP_ASSERT("dhcp_create_msg: check that first pbuf can hold struct dhcp_msg",
+ (dhcp->p_out->len >= sizeof(struct dhcp_msg)));
+
+ /* DHCP_REQUEST should reuse 'xid' from DHCPOFFER */
+ if (message_type != DHCP_REQUEST) {
+ /* reuse transaction identifier in retransmissions */
+ if (dhcp->tries == 0) {
+#if DHCP_CREATE_RAND_XID && defined(LWIP_RAND)
+ xid = LWIP_RAND();
+#else /* DHCP_CREATE_RAND_XID && defined(LWIP_RAND) */
+ xid++;
+#endif /* DHCP_CREATE_RAND_XID && defined(LWIP_RAND) */
+ }
+ dhcp->xid = xid;
+ }
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE,
+ ("transaction id xid(%"X32_F")\n", xid));
+
+ dhcp->msg_out = (struct dhcp_msg *)dhcp->p_out->payload;
+
+ dhcp->msg_out->op = DHCP_BOOTREQUEST;
+ /* @todo: make link layer independent */
+ dhcp->msg_out->htype = DHCP_HTYPE_ETH;
+ dhcp->msg_out->hlen = netif->hwaddr_len;
+ dhcp->msg_out->hops = 0;
+ dhcp->msg_out->xid = lwip_htonl(dhcp->xid);
+ dhcp->msg_out->secs = 0;
+ /* we don't need the broadcast flag since we can receive unicast traffic
+ before being fully configured! */
+ dhcp->msg_out->flags = 0;
+ ip4_addr_set_zero(&dhcp->msg_out->ciaddr);
+ /* set ciaddr to netif->ip_addr based on message_type and state */
+ if ((message_type == DHCP_INFORM) || (message_type == DHCP_DECLINE) || (message_type == DHCP_RELEASE) ||
+ ((message_type == DHCP_REQUEST) && /* DHCP_STATE_BOUND not used for sending! */
+ ((dhcp->state== DHCP_STATE_RENEWING) || dhcp->state== DHCP_STATE_REBINDING))) {
+ ip4_addr_copy(dhcp->msg_out->ciaddr, *netif_ip4_addr(netif));
+ }
+ ip4_addr_set_zero(&dhcp->msg_out->yiaddr);
+ ip4_addr_set_zero(&dhcp->msg_out->siaddr);
+ ip4_addr_set_zero(&dhcp->msg_out->giaddr);
+ for (i = 0; i < DHCP_CHADDR_LEN; i++) {
+ /* copy netif hardware address, pad with zeroes */
+ dhcp->msg_out->chaddr[i] = (i < netif->hwaddr_len && i < NETIF_MAX_HWADDR_LEN) ? netif->hwaddr[i] : 0/* pad byte*/;
+ }
+ for (i = 0; i < DHCP_SNAME_LEN; i++) {
+ dhcp->msg_out->sname[i] = 0;
+ }
+ for (i = 0; i < DHCP_FILE_LEN; i++) {
+ dhcp->msg_out->file[i] = 0;
+ }
+ dhcp->msg_out->cookie = PP_HTONL(DHCP_MAGIC_COOKIE);
+ dhcp->options_out_len = 0;
+ /* fill options field with an incrementing array (for debugging purposes) */
+ for (i = 0; i < DHCP_OPTIONS_LEN; i++) {
+ dhcp->msg_out->options[i] = (u8_t)i; /* for debugging only, no matter if truncated */
+ }
+ /* Add option MESSAGE_TYPE */
+ dhcp_option(dhcp, DHCP_OPTION_MESSAGE_TYPE, DHCP_OPTION_MESSAGE_TYPE_LEN);
+ dhcp_option_byte(dhcp, message_type);
+ return ERR_OK;
+}
+
+/**
+ * Free previously allocated memory used to send a DHCP request.
+ *
+ * @param dhcp the dhcp struct to free the request from
+ */
+static void
+dhcp_delete_msg(struct dhcp *dhcp)
+{
+ LWIP_ERROR("dhcp_delete_msg: dhcp != NULL", (dhcp != NULL), return;);
+ LWIP_ASSERT("dhcp_delete_msg: dhcp->p_out != NULL", dhcp->p_out != NULL);
+ LWIP_ASSERT("dhcp_delete_msg: dhcp->msg_out != NULL", dhcp->msg_out != NULL);
+ if (dhcp->p_out != NULL) {
+ pbuf_free(dhcp->p_out);
+ }
+ dhcp->p_out = NULL;
+ dhcp->msg_out = NULL;
+}
+
+/**
+ * Add a DHCP message trailer
+ *
+ * Adds the END option to the DHCP message, and if
+ * necessary, up to three padding bytes.
+ *
+ * @param dhcp DHCP state structure
+ */
+static void
+dhcp_option_trailer(struct dhcp *dhcp)
+{
+ LWIP_ERROR("dhcp_option_trailer: dhcp != NULL", (dhcp != NULL), return;);
+ LWIP_ASSERT("dhcp_option_trailer: dhcp->msg_out != NULL\n", dhcp->msg_out != NULL);
+ LWIP_ASSERT("dhcp_option_trailer: dhcp->options_out_len < DHCP_OPTIONS_LEN\n", dhcp->options_out_len < DHCP_OPTIONS_LEN);
+ dhcp->msg_out->options[dhcp->options_out_len++] = DHCP_OPTION_END;
+ /* packet is too small, or not 4 byte aligned? */
+ while (((dhcp->options_out_len < DHCP_MIN_OPTIONS_LEN) || (dhcp->options_out_len & 3)) &&
+ (dhcp->options_out_len < DHCP_OPTIONS_LEN)) {
+ /* add a fill/padding byte */
+ dhcp->msg_out->options[dhcp->options_out_len++] = 0;
+ }
+}
+
+/** check if DHCP supplied netif->ip_addr
+ *
+ * @param netif the netif to check
+ * @return 1 if DHCP supplied netif->ip_addr (states BOUND or RENEWING),
+ * 0 otherwise
+ */
+u8_t
+dhcp_supplied_address(const struct netif *netif)
+{
+ if ((netif != NULL) && (netif_dhcp_data(netif) != NULL)) {
+ struct dhcp* dhcp = netif_dhcp_data(netif);
+ return (dhcp->state == DHCP_STATE_BOUND) || (dhcp->state == DHCP_STATE_RENEWING);
+ }
+ return 0;
+}
+
+#endif /* LWIP_IPV4 && LWIP_DHCP */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/ipv4/etharp.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/ipv4/etharp.c
new file mode 100644
index 0000000..3f48a99
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/ipv4/etharp.c
@@ -0,0 +1,1206 @@
+/**
+ * @file
+ * Address Resolution Protocol module for IP over Ethernet
+ *
+ * Functionally, ARP is divided into two parts. The first maps an IP address
+ * to a physical address when sending a packet, and the second part answers
+ * requests from other machines for our physical address.
+ *
+ * This implementation complies with RFC 826 (Ethernet ARP). It supports
+ * Gratuitious ARP from RFC3220 (IP Mobility Support for IPv4) section 4.6
+ * if an interface calls etharp_gratuitous(our_netif) upon address change.
+ */
+
+/*
+ * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
+ * Copyright (c) 2003-2004 Leon Woestenberg <leon.woestenberg@axon.tv>
+ * Copyright (c) 2003-2004 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_ARP || LWIP_ETHERNET
+
+#include "lwip/etharp.h"
+#include "lwip/stats.h"
+#include "lwip/snmp.h"
+#include "lwip/dhcp.h"
+#include "lwip/autoip.h"
+#include "netif/ethernet.h"
+
+#include <string.h>
+
+#ifdef LWIP_HOOK_FILENAME
+#include LWIP_HOOK_FILENAME
+#endif
+
+#if LWIP_IPV4 && LWIP_ARP /* don't build if not configured for use in lwipopts.h */
+
+/** 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_UNICAST (ARP_MAXAGE - 30)
+#define ARP_AGE_REREQUEST_USED_BROADCAST (ARP_MAXAGE - 15)
+
+/** the time an ARP entry stays pending after first request,
+ * for ARP_TMR_INTERVAL = 1000, this is
+ * 10 seconds.
+ *
+ * @internal Keep this number at least 2, otherwise it might
+ * run out instantly if the timeout occurs directly after a request.
+ */
+#define ARP_MAXPENDING 5
+
+/** ARP states */
+enum etharp_state {
+ ETHARP_STATE_EMPTY = 0,
+ ETHARP_STATE_PENDING,
+ ETHARP_STATE_STABLE,
+ ETHARP_STATE_STABLE_REREQUESTING_1,
+ ETHARP_STATE_STABLE_REREQUESTING_2
+#if ETHARP_SUPPORT_STATIC_ENTRIES
+ ,ETHARP_STATE_STATIC
+#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
+};
+
+struct etharp_entry {
+#if ARP_QUEUEING
+ /** Pointer to queue of pending outgoing packets on this ARP entry. */
+ struct etharp_q_entry *q;
+#else /* ARP_QUEUEING */
+ /** Pointer to a single pending outgoing packet on this ARP entry. */
+ struct pbuf *q;
+#endif /* ARP_QUEUEING */
+ ip4_addr_t ipaddr;
+ struct netif *netif;
+ struct eth_addr ethaddr;
+ u16_t ctime;
+ u8_t state;
+};
+
+static struct etharp_entry arp_table[ARP_TABLE_SIZE];
+
+#if !LWIP_NETIF_HWADDRHINT
+static u8_t etharp_cached_entry;
+#endif /* !LWIP_NETIF_HWADDRHINT */
+
+/** Try hard to create a new entry - we want the IP address to appear in
+ the cache (even if this means removing an active entry or so). */
+#define ETHARP_FLAG_TRY_HARD 1
+#define ETHARP_FLAG_FIND_ONLY 2
+#if ETHARP_SUPPORT_STATIC_ENTRIES
+#define ETHARP_FLAG_STATIC_ENTRY 4
+#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
+
+#if LWIP_NETIF_HWADDRHINT
+#define ETHARP_SET_HINT(netif, hint) if (((netif) != NULL) && ((netif)->addr_hint != NULL)) \
+ *((netif)->addr_hint) = (hint);
+#else /* LWIP_NETIF_HWADDRHINT */
+#define ETHARP_SET_HINT(netif, hint) (etharp_cached_entry = (hint))
+#endif /* LWIP_NETIF_HWADDRHINT */
+
+
+/* Some checks, instead of etharp_init(): */
+#if (LWIP_ARP && (ARP_TABLE_SIZE > 0x7f))
+ #error "ARP_TABLE_SIZE must fit in an s8_t, you have to reduce it in your lwipopts.h"
+#endif
+
+
+static err_t etharp_request_dst(struct netif *netif, const ip4_addr_t *ipaddr, const struct eth_addr* hw_dst_addr);
+static err_t etharp_raw(struct netif *netif,
+ const struct eth_addr *ethsrc_addr, const struct eth_addr *ethdst_addr,
+ const struct eth_addr *hwsrc_addr, const ip4_addr_t *ipsrc_addr,
+ const struct eth_addr *hwdst_addr, const ip4_addr_t *ipdst_addr,
+ const u16_t opcode);
+
+#if ARP_QUEUEING
+/**
+ * Free a complete queue of etharp entries
+ *
+ * @param q a qeueue of etharp_q_entry's to free
+ */
+static void
+free_etharp_q(struct etharp_q_entry *q)
+{
+ struct etharp_q_entry *r;
+ LWIP_ASSERT("q != NULL", q != NULL);
+ LWIP_ASSERT("q->p != NULL", q->p != NULL);
+ while (q) {
+ r = q;
+ q = q->next;
+ LWIP_ASSERT("r->p != NULL", (r->p != NULL));
+ pbuf_free(r->p);
+ memp_free(MEMP_ARP_QUEUE, r);
+ }
+}
+#else /* ARP_QUEUEING */
+
+/** Compatibility define: free the queued pbuf */
+#define free_etharp_q(q) pbuf_free(q)
+
+#endif /* ARP_QUEUEING */
+
+/** Clean up ARP table entries */
+static void
+etharp_free_entry(int i)
+{
+ /* remove from SNMP ARP index tree */
+ mib2_remove_arp_entry(arp_table[i].netif, &arp_table[i].ipaddr);
+ /* and empty packet queue */
+ if (arp_table[i].q != NULL) {
+ /* remove all queued packets */
+ LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_free_entry: freeing entry %"U16_F", packet queue %p.\n", (u16_t)i, (void *)(arp_table[i].q)));
+ free_etharp_q(arp_table[i].q);
+ arp_table[i].q = NULL;
+ }
+ /* recycle entry for re-use */
+ arp_table[i].state = ETHARP_STATE_EMPTY;
+#ifdef LWIP_DEBUG
+ /* for debugging, clean out the complete entry */
+ arp_table[i].ctime = 0;
+ arp_table[i].netif = NULL;
+ ip4_addr_set_zero(&arp_table[i].ipaddr);
+ arp_table[i].ethaddr = ethzero;
+#endif /* LWIP_DEBUG */
+}
+
+/**
+ * Clears expired entries in the ARP table.
+ *
+ * This function should be called every ARP_TMR_INTERVAL milliseconds (1 second),
+ * in order to expire entries in the ARP table.
+ */
+void
+etharp_tmr(void)
+{
+ u8_t i;
+
+ LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_timer\n"));
+ /* remove expired entries from the ARP table */
+ for (i = 0; i < ARP_TABLE_SIZE; ++i) {
+ u8_t state = arp_table[i].state;
+ if (state != ETHARP_STATE_EMPTY
+#if ETHARP_SUPPORT_STATIC_ENTRIES
+ && (state != ETHARP_STATE_STATIC)
+#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
+ ) {
+ arp_table[i].ctime++;
+ if ((arp_table[i].ctime >= ARP_MAXAGE) ||
+ ((arp_table[i].state == ETHARP_STATE_PENDING) &&
+ (arp_table[i].ctime >= ARP_MAXPENDING))) {
+ /* pending or stable entry has become old! */
+ LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_timer: expired %s entry %"U16_F".\n",
+ arp_table[i].state >= ETHARP_STATE_STABLE ? "stable" : "pending", (u16_t)i));
+ /* clean up entries that have just been expired */
+ etharp_free_entry(i);
+ } else if (arp_table[i].state == ETHARP_STATE_STABLE_REREQUESTING_1) {
+ /* Don't send more than one request every 2 seconds. */
+ arp_table[i].state = ETHARP_STATE_STABLE_REREQUESTING_2;
+ } else if (arp_table[i].state == ETHARP_STATE_STABLE_REREQUESTING_2) {
+ /* Reset state to stable, so that the next transmitted packet will
+ re-send an ARP request. */
+ arp_table[i].state = ETHARP_STATE_STABLE;
+ } else if (arp_table[i].state == ETHARP_STATE_PENDING) {
+ /* still pending, resend an ARP query */
+ etharp_request(arp_table[i].netif, &arp_table[i].ipaddr);
+ }
+ }
+ }
+}
+
+/**
+ * Search the ARP table for a matching or new entry.
+ *
+ * If an IP address is given, return a pending or stable ARP entry that matches
+ * the address. If no match is found, create a new entry with this address set,
+ * but in state ETHARP_EMPTY. The caller must check and possibly change the
+ * state of the returned entry.
+ *
+ * If ipaddr is NULL, return a initialized new entry in state ETHARP_EMPTY.
+ *
+ * In all cases, attempt to create new entries from an empty entry. If no
+ * empty entries are available and ETHARP_FLAG_TRY_HARD flag is set, recycle
+ * old entries. Heuristic choose the least important entry for recycling.
+ *
+ * @param ipaddr IP address to find in ARP cache, or to add if not found.
+ * @param flags See @ref etharp_state
+ * @param netif netif related to this address (used for NETIF_HWADDRHINT)
+ *
+ * @return The ARP entry index that matched or is created, ERR_MEM if no
+ * entry is found or could be recycled.
+ */
+static s8_t
+etharp_find_entry(const ip4_addr_t *ipaddr, u8_t flags, struct netif* netif)
+{
+ s8_t old_pending = ARP_TABLE_SIZE, old_stable = ARP_TABLE_SIZE;
+ s8_t empty = ARP_TABLE_SIZE;
+ u8_t i = 0;
+ /* oldest entry with packets on queue */
+ s8_t old_queue = ARP_TABLE_SIZE;
+ /* its age */
+ u16_t age_queue = 0, age_pending = 0, age_stable = 0;
+
+ LWIP_UNUSED_ARG(netif);
+
+ /**
+ * a) do a search through the cache, remember candidates
+ * b) select candidate entry
+ * c) create new entry
+ */
+
+ /* a) in a single search sweep, do all of this
+ * 1) remember the first empty entry (if any)
+ * 2) remember the oldest stable entry (if any)
+ * 3) remember the oldest pending entry without queued packets (if any)
+ * 4) remember the oldest pending entry with queued packets (if any)
+ * 5) search for a matching IP entry, either pending or stable
+ * until 5 matches, or all entries are searched for.
+ */
+
+ for (i = 0; i < ARP_TABLE_SIZE; ++i) {
+ u8_t state = arp_table[i].state;
+ /* no empty entry found yet and now we do find one? */
+ if ((empty == ARP_TABLE_SIZE) && (state == ETHARP_STATE_EMPTY)) {
+ LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_find_entry: found empty entry %"U16_F"\n", (u16_t)i));
+ /* remember first empty entry */
+ empty = i;
+ } else if (state != ETHARP_STATE_EMPTY) {
+ LWIP_ASSERT("state == ETHARP_STATE_PENDING || state >= ETHARP_STATE_STABLE",
+ state == ETHARP_STATE_PENDING || state >= ETHARP_STATE_STABLE);
+ /* if given, does IP address match IP address in ARP entry? */
+ if (ipaddr && ip4_addr_cmp(ipaddr, &arp_table[i].ipaddr)
+#if ETHARP_TABLE_MATCH_NETIF
+ && ((netif == NULL) || (netif == arp_table[i].netif))
+#endif /* ETHARP_TABLE_MATCH_NETIF */
+ ) {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: found matching entry %"U16_F"\n", (u16_t)i));
+ /* found exact IP address match, simply bail out */
+ return i;
+ }
+ /* pending entry? */
+ if (state == ETHARP_STATE_PENDING) {
+ /* pending with queued packets? */
+ if (arp_table[i].q != NULL) {
+ if (arp_table[i].ctime >= age_queue) {
+ old_queue = i;
+ age_queue = arp_table[i].ctime;
+ }
+ } else
+ /* pending without queued packets? */
+ {
+ if (arp_table[i].ctime >= age_pending) {
+ old_pending = i;
+ age_pending = arp_table[i].ctime;
+ }
+ }
+ /* stable entry? */
+ } else if (state >= ETHARP_STATE_STABLE) {
+#if ETHARP_SUPPORT_STATIC_ENTRIES
+ /* don't record old_stable for static entries since they never expire */
+ if (state < ETHARP_STATE_STATIC)
+#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
+ {
+ /* remember entry with oldest stable entry in oldest, its age in maxtime */
+ if (arp_table[i].ctime >= age_stable) {
+ old_stable = i;
+ age_stable = arp_table[i].ctime;
+ }
+ }
+ }
+ }
+ }
+ /* { we have no match } => try to create a new entry */
+
+ /* don't create new entry, only search? */
+ if (((flags & ETHARP_FLAG_FIND_ONLY) != 0) ||
+ /* or no empty entry found and not allowed to recycle? */
+ ((empty == ARP_TABLE_SIZE) && ((flags & ETHARP_FLAG_TRY_HARD) == 0))) {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: no empty entry found and not allowed to recycle\n"));
+ return (s8_t)ERR_MEM;
+ }
+
+ /* b) choose the least destructive entry to recycle:
+ * 1) empty entry
+ * 2) oldest stable entry
+ * 3) oldest pending entry without queued packets
+ * 4) oldest pending entry with queued packets
+ *
+ * { ETHARP_FLAG_TRY_HARD is set at this point }
+ */
+
+ /* 1) empty entry available? */
+ if (empty < ARP_TABLE_SIZE) {
+ i = empty;
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: selecting empty entry %"U16_F"\n", (u16_t)i));
+ } else {
+ /* 2) found recyclable stable entry? */
+ if (old_stable < ARP_TABLE_SIZE) {
+ /* recycle oldest stable*/
+ i = old_stable;
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: selecting oldest stable entry %"U16_F"\n", (u16_t)i));
+ /* no queued packets should exist on stable entries */
+ LWIP_ASSERT("arp_table[i].q == NULL", arp_table[i].q == NULL);
+ /* 3) found recyclable pending entry without queued packets? */
+ } else if (old_pending < ARP_TABLE_SIZE) {
+ /* recycle oldest pending */
+ i = old_pending;
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: selecting oldest pending entry %"U16_F" (without queue)\n", (u16_t)i));
+ /* 4) found recyclable pending entry with queued packets? */
+ } else if (old_queue < ARP_TABLE_SIZE) {
+ /* recycle oldest pending (queued packets are free in etharp_free_entry) */
+ i = old_queue;
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: selecting oldest pending entry %"U16_F", freeing packet queue %p\n", (u16_t)i, (void *)(arp_table[i].q)));
+ /* no empty or recyclable entries found */
+ } else {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: no empty or recyclable entries found\n"));
+ return (s8_t)ERR_MEM;
+ }
+
+ /* { empty or recyclable entry found } */
+ LWIP_ASSERT("i < ARP_TABLE_SIZE", i < ARP_TABLE_SIZE);
+ etharp_free_entry(i);
+ }
+
+ LWIP_ASSERT("i < ARP_TABLE_SIZE", i < ARP_TABLE_SIZE);
+ LWIP_ASSERT("arp_table[i].state == ETHARP_STATE_EMPTY",
+ arp_table[i].state == ETHARP_STATE_EMPTY);
+
+ /* IP address given? */
+ if (ipaddr != NULL) {
+ /* set IP address */
+ ip4_addr_copy(arp_table[i].ipaddr, *ipaddr);
+ }
+ arp_table[i].ctime = 0;
+#if ETHARP_TABLE_MATCH_NETIF
+ arp_table[i].netif = netif;
+#endif /* ETHARP_TABLE_MATCH_NETIF*/
+ return (err_t)i;
+}
+
+/**
+ * Update (or insert) a IP/MAC address pair in the ARP cache.
+ *
+ * If a pending entry is resolved, any queued packets will be sent
+ * at this point.
+ *
+ * @param netif netif related to this entry (used for NETIF_ADDRHINT)
+ * @param ipaddr IP address of the inserted ARP entry.
+ * @param ethaddr Ethernet address of the inserted ARP entry.
+ * @param flags See @ref etharp_state
+ *
+ * @return
+ * - ERR_OK Successfully updated ARP cache.
+ * - ERR_MEM If we could not add a new ARP entry when ETHARP_FLAG_TRY_HARD was set.
+ * - ERR_ARG Non-unicast address given, those will not appear in ARP cache.
+ *
+ * @see pbuf_free()
+ */
+static err_t
+etharp_update_arp_entry(struct netif *netif, const ip4_addr_t *ipaddr, struct eth_addr *ethaddr, u8_t flags)
+{
+ s8_t i;
+ LWIP_ASSERT("netif->hwaddr_len == ETH_HWADDR_LEN", netif->hwaddr_len == ETH_HWADDR_LEN);
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_update_arp_entry: %"U16_F".%"U16_F".%"U16_F".%"U16_F" - %02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F"\n",
+ ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr), ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr),
+ (u16_t)ethaddr->addr[0], (u16_t)ethaddr->addr[1], (u16_t)ethaddr->addr[2],
+ (u16_t)ethaddr->addr[3], (u16_t)ethaddr->addr[4], (u16_t)ethaddr->addr[5]));
+ /* non-unicast address? */
+ if (ip4_addr_isany(ipaddr) ||
+ ip4_addr_isbroadcast(ipaddr, netif) ||
+ ip4_addr_ismulticast(ipaddr)) {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_update_arp_entry: will not add non-unicast IP address to ARP cache\n"));
+ return ERR_ARG;
+ }
+ /* find or create ARP entry */
+ i = etharp_find_entry(ipaddr, flags, netif);
+ /* bail out if no entry could be found */
+ if (i < 0) {
+ return (err_t)i;
+ }
+
+#if ETHARP_SUPPORT_STATIC_ENTRIES
+ if (flags & ETHARP_FLAG_STATIC_ENTRY) {
+ /* record static type */
+ arp_table[i].state = ETHARP_STATE_STATIC;
+ } else if (arp_table[i].state == ETHARP_STATE_STATIC) {
+ /* found entry is a static type, don't overwrite it */
+ return ERR_VAL;
+ } else
+#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
+ {
+ /* mark it stable */
+ arp_table[i].state = ETHARP_STATE_STABLE;
+ }
+
+ /* record network interface */
+ arp_table[i].netif = netif;
+ /* insert in SNMP ARP index tree */
+ mib2_add_arp_entry(netif, &arp_table[i].ipaddr);
+
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_update_arp_entry: updating stable entry %"S16_F"\n", (s16_t)i));
+ /* update address */
+ ETHADDR32_COPY(&arp_table[i].ethaddr, ethaddr);
+ /* reset time stamp */
+ arp_table[i].ctime = 0;
+ /* this is where we will send out queued packets! */
+#if ARP_QUEUEING
+ while (arp_table[i].q != NULL) {
+ struct pbuf *p;
+ /* remember remainder of queue */
+ struct etharp_q_entry *q = arp_table[i].q;
+ /* pop first item off the queue */
+ arp_table[i].q = q->next;
+ /* get the packet pointer */
+ p = q->p;
+ /* now queue entry can be freed */
+ memp_free(MEMP_ARP_QUEUE, q);
+#else /* ARP_QUEUEING */
+ if (arp_table[i].q != NULL) {
+ struct pbuf *p = arp_table[i].q;
+ arp_table[i].q = NULL;
+#endif /* ARP_QUEUEING */
+ /* send the queued IP packet */
+ ethernet_output(netif, p, (struct eth_addr*)(netif->hwaddr), ethaddr, ETHTYPE_IP);
+ /* free the queued IP packet */
+ pbuf_free(p);
+ }
+ return ERR_OK;
+}
+
+#if ETHARP_SUPPORT_STATIC_ENTRIES
+/** Add a new static entry to the ARP table. If an entry exists for the
+ * specified IP address, this entry is overwritten.
+ * If packets are queued for the specified IP address, they are sent out.
+ *
+ * @param ipaddr IP address for the new static entry
+ * @param ethaddr ethernet address for the new static entry
+ * @return See return values of etharp_add_static_entry
+ */
+err_t
+etharp_add_static_entry(const ip4_addr_t *ipaddr, struct eth_addr *ethaddr)
+{
+ struct netif *netif;
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_add_static_entry: %"U16_F".%"U16_F".%"U16_F".%"U16_F" - %02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F"\n",
+ ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr), ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr),
+ (u16_t)ethaddr->addr[0], (u16_t)ethaddr->addr[1], (u16_t)ethaddr->addr[2],
+ (u16_t)ethaddr->addr[3], (u16_t)ethaddr->addr[4], (u16_t)ethaddr->addr[5]));
+
+ netif = ip4_route(ipaddr);
+ if (netif == NULL) {
+ return ERR_RTE;
+ }
+
+ return etharp_update_arp_entry(netif, ipaddr, ethaddr, ETHARP_FLAG_TRY_HARD | ETHARP_FLAG_STATIC_ENTRY);
+}
+
+/** Remove a static entry from the ARP table previously added with a call to
+ * etharp_add_static_entry.
+ *
+ * @param ipaddr IP address of the static entry to remove
+ * @return ERR_OK: entry removed
+ * ERR_MEM: entry wasn't found
+ * ERR_ARG: entry wasn't a static entry but a dynamic one
+ */
+err_t
+etharp_remove_static_entry(const ip4_addr_t *ipaddr)
+{
+ s8_t i;
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_remove_static_entry: %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr), ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr)));
+
+ /* find or create ARP entry */
+ i = etharp_find_entry(ipaddr, ETHARP_FLAG_FIND_ONLY, NULL);
+ /* bail out if no entry could be found */
+ if (i < 0) {
+ return (err_t)i;
+ }
+
+ if (arp_table[i].state != ETHARP_STATE_STATIC) {
+ /* entry wasn't a static entry, cannot remove it */
+ return ERR_ARG;
+ }
+ /* entry found, free it */
+ etharp_free_entry(i);
+ return ERR_OK;
+}
+#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
+
+/**
+ * Remove all ARP table entries of the specified netif.
+ *
+ * @param netif points to a network interface
+ */
+void
+etharp_cleanup_netif(struct netif *netif)
+{
+ u8_t i;
+
+ for (i = 0; i < ARP_TABLE_SIZE; ++i) {
+ u8_t state = arp_table[i].state;
+ if ((state != ETHARP_STATE_EMPTY) && (arp_table[i].netif == netif)) {
+ etharp_free_entry(i);
+ }
+ }
+}
+
+/**
+ * Finds (stable) ethernet/IP address pair from ARP table
+ * using interface and IP address index.
+ * @note the addresses in the ARP table are in network order!
+ *
+ * @param netif points to interface index
+ * @param ipaddr points to the (network order) IP address index
+ * @param eth_ret points to return pointer
+ * @param ip_ret points to return pointer
+ * @return table index if found, -1 otherwise
+ */
+s8_t
+etharp_find_addr(struct netif *netif, const ip4_addr_t *ipaddr,
+ struct eth_addr **eth_ret, const ip4_addr_t **ip_ret)
+{
+ s8_t i;
+
+ LWIP_ASSERT("eth_ret != NULL && ip_ret != NULL",
+ eth_ret != NULL && ip_ret != NULL);
+
+ LWIP_UNUSED_ARG(netif);
+
+ i = etharp_find_entry(ipaddr, ETHARP_FLAG_FIND_ONLY, netif);
+ if ((i >= 0) && (arp_table[i].state >= ETHARP_STATE_STABLE)) {
+ *eth_ret = &arp_table[i].ethaddr;
+ *ip_ret = &arp_table[i].ipaddr;
+ return i;
+ }
+ return -1;
+}
+
+/**
+ * Possibility to iterate over stable ARP table entries
+ *
+ * @param i entry number, 0 to ARP_TABLE_SIZE
+ * @param ipaddr return value: IP address
+ * @param netif return value: points to interface
+ * @param eth_ret return value: ETH address
+ * @return 1 on valid index, 0 otherwise
+ */
+u8_t
+etharp_get_entry(u8_t i, ip4_addr_t **ipaddr, struct netif **netif, struct eth_addr **eth_ret)
+{
+ LWIP_ASSERT("ipaddr != NULL", ipaddr != NULL);
+ LWIP_ASSERT("netif != NULL", netif != NULL);
+ LWIP_ASSERT("eth_ret != NULL", eth_ret != NULL);
+
+ if((i < ARP_TABLE_SIZE) && (arp_table[i].state >= ETHARP_STATE_STABLE)) {
+ *ipaddr = &arp_table[i].ipaddr;
+ *netif = arp_table[i].netif;
+ *eth_ret = &arp_table[i].ethaddr;
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+/**
+ * Responds to ARP requests to us. Upon ARP replies to us, add entry to cache
+ * send out queued IP packets. Updates cache with snooped address pairs.
+ *
+ * Should be called for incoming ARP packets. The pbuf in the argument
+ * is freed by this function.
+ *
+ * @param p The ARP packet that arrived on netif. Is freed by this function.
+ * @param netif The lwIP network interface on which the ARP packet pbuf arrived.
+ *
+ * @see pbuf_free()
+ */
+void
+etharp_input(struct pbuf *p, struct netif *netif)
+{
+ struct etharp_hdr *hdr;
+ /* these are aligned properly, whereas the ARP header fields might not be */
+ ip4_addr_t sipaddr, dipaddr;
+ u8_t for_us;
+
+ LWIP_ERROR("netif != NULL", (netif != NULL), return;);
+
+ hdr = (struct etharp_hdr *)p->payload;
+
+ /* RFC 826 "Packet Reception": */
+ if ((hdr->hwtype != PP_HTONS(HWTYPE_ETHERNET)) ||
+ (hdr->hwlen != ETH_HWADDR_LEN) ||
+ (hdr->protolen != sizeof(ip4_addr_t)) ||
+ (hdr->proto != PP_HTONS(ETHTYPE_IP))) {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING,
+ ("etharp_input: packet dropped, wrong hw type, hwlen, proto, protolen or ethernet type (%"U16_F"/%"U16_F"/%"U16_F"/%"U16_F")\n",
+ hdr->hwtype, (u16_t)hdr->hwlen, hdr->proto, (u16_t)hdr->protolen));
+ ETHARP_STATS_INC(etharp.proterr);
+ ETHARP_STATS_INC(etharp.drop);
+ pbuf_free(p);
+ return;
+ }
+ ETHARP_STATS_INC(etharp.recv);
+
+#if LWIP_AUTOIP
+ /* We have to check if a host already has configured our random
+ * created link local address and continuously check if there is
+ * a host with this IP-address so we can detect collisions */
+ autoip_arp_reply(netif, hdr);
+#endif /* LWIP_AUTOIP */
+
+ /* Copy struct ip4_addr2 to aligned ip4_addr, to support compilers without
+ * structure packing (not using structure copy which breaks strict-aliasing rules). */
+ IPADDR2_COPY(&sipaddr, &hdr->sipaddr);
+ IPADDR2_COPY(&dipaddr, &hdr->dipaddr);
+
+ /* this interface is not configured? */
+ if (ip4_addr_isany_val(*netif_ip4_addr(netif))) {
+ for_us = 0;
+ } else {
+ /* ARP packet directed to us? */
+ for_us = (u8_t)ip4_addr_cmp(&dipaddr, netif_ip4_addr(netif));
+ }
+
+ /* ARP message directed to us?
+ -> add IP address in ARP cache; assume requester wants to talk to us,
+ can result in directly sending the queued packets for this host.
+ ARP message not directed to us?
+ -> update the source IP address in the cache, if present */
+ etharp_update_arp_entry(netif, &sipaddr, &(hdr->shwaddr),
+ for_us ? ETHARP_FLAG_TRY_HARD : ETHARP_FLAG_FIND_ONLY);
+
+ /* now act on the message itself */
+ switch (hdr->opcode) {
+ /* ARP request? */
+ case PP_HTONS(ARP_REQUEST):
+ /* ARP request. If it asked for our address, we send out a
+ * reply. In any case, we time-stamp any existing ARP entry,
+ * and possibly send out an IP packet that was queued on it. */
+
+ LWIP_DEBUGF (ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_input: incoming ARP request\n"));
+ /* ARP request for our address? */
+ if (for_us) {
+ /* send ARP response */
+ etharp_raw(netif,
+ (struct eth_addr *)netif->hwaddr, &hdr->shwaddr,
+ (struct eth_addr *)netif->hwaddr, netif_ip4_addr(netif),
+ &hdr->shwaddr, &sipaddr,
+ ARP_REPLY);
+ /* we are not configured? */
+ } else if (ip4_addr_isany_val(*netif_ip4_addr(netif))) {
+ /* { for_us == 0 and netif->ip_addr.addr == 0 } */
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_input: we are unconfigured, ARP request ignored.\n"));
+ /* request was not directed to us */
+ } else {
+ /* { for_us == 0 and netif->ip_addr.addr != 0 } */
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_input: ARP request was not for us.\n"));
+ }
+ break;
+ case PP_HTONS(ARP_REPLY):
+ /* ARP reply. We already updated the ARP cache earlier. */
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_input: incoming ARP reply\n"));
+#if (LWIP_DHCP && DHCP_DOES_ARP_CHECK)
+ /* DHCP wants to know about ARP replies from any host with an
+ * IP address also offered to us by the DHCP server. We do not
+ * want to take a duplicate IP address on a single network.
+ * @todo How should we handle redundant (fail-over) interfaces? */
+ dhcp_arp_reply(netif, &sipaddr);
+#endif /* (LWIP_DHCP && DHCP_DOES_ARP_CHECK) */
+ break;
+ default:
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_input: ARP unknown opcode type %"S16_F"\n", lwip_htons(hdr->opcode)));
+ ETHARP_STATS_INC(etharp.err);
+ break;
+ }
+ /* free ARP packet */
+ pbuf_free(p);
+}
+
+/** Just a small helper function that sends a pbuf to an ethernet address
+ * in the arp_table specified by the index 'arp_idx'.
+ */
+static err_t
+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) {
+ if (arp_table[arp_idx].ctime >= ARP_AGE_REREQUEST_USED_BROADCAST) {
+ /* issue a standard request using broadcast */
+ if (etharp_request(netif, &arp_table[arp_idx].ipaddr) == ERR_OK) {
+ arp_table[arp_idx].state = ETHARP_STATE_STABLE_REREQUESTING_1;
+ }
+ } else if (arp_table[arp_idx].ctime >= ARP_AGE_REREQUEST_USED_UNICAST) {
+ /* issue a unicast request (for 15 seconds) to prevent unnecessary broadcast */
+ if (etharp_request_dst(netif, &arp_table[arp_idx].ipaddr, &arp_table[arp_idx].ethaddr) == ERR_OK) {
+ arp_table[arp_idx].state = ETHARP_STATE_STABLE_REREQUESTING_1;
+ }
+ }
+ }
+
+ return ethernet_output(netif, q, (struct eth_addr*)(netif->hwaddr), &arp_table[arp_idx].ethaddr, ETHTYPE_IP);
+}
+
+/**
+ * Resolve and fill-in Ethernet address header for outgoing IP packet.
+ *
+ * For IP multicast and broadcast, corresponding Ethernet addresses
+ * are selected and the packet is transmitted on the link.
+ *
+ * For unicast addresses, the packet is submitted to etharp_query(). In
+ * case the IP address is outside the local network, the IP address of
+ * the gateway is used.
+ *
+ * @param netif The lwIP network interface which the IP packet will be sent on.
+ * @param q The pbuf(s) containing the IP packet to be sent.
+ * @param ipaddr The IP address of the packet destination.
+ *
+ * @return
+ * - ERR_RTE No route to destination (no gateway to external networks),
+ * or the return type of either etharp_query() or ethernet_output().
+ */
+err_t
+etharp_output(struct netif *netif, struct pbuf *q, const ip4_addr_t *ipaddr)
+{
+ const struct eth_addr *dest;
+ struct eth_addr mcastaddr;
+ const ip4_addr_t *dst_addr = ipaddr;
+
+ LWIP_ASSERT("netif != NULL", netif != NULL);
+ LWIP_ASSERT("q != NULL", q != NULL);
+ LWIP_ASSERT("ipaddr != NULL", ipaddr != NULL);
+
+ /* Determine on destination hardware address. Broadcasts and multicasts
+ * are special, other IP addresses are looked up in the ARP table. */
+
+ /* broadcast destination IP address? */
+ if (ip4_addr_isbroadcast(ipaddr, netif)) {
+ /* broadcast on Ethernet also */
+ dest = (const struct eth_addr *)&ethbroadcast;
+ /* multicast destination IP address? */
+ } else if (ip4_addr_ismulticast(ipaddr)) {
+ /* Hash IP multicast address to MAC address.*/
+ mcastaddr.addr[0] = LL_IP4_MULTICAST_ADDR_0;
+ mcastaddr.addr[1] = LL_IP4_MULTICAST_ADDR_1;
+ mcastaddr.addr[2] = LL_IP4_MULTICAST_ADDR_2;
+ mcastaddr.addr[3] = ip4_addr2(ipaddr) & 0x7f;
+ mcastaddr.addr[4] = ip4_addr3(ipaddr);
+ mcastaddr.addr[5] = ip4_addr4(ipaddr);
+ /* destination Ethernet address is multicast */
+ dest = &mcastaddr;
+ /* unicast destination IP address? */
+ } else {
+ s8_t i;
+ /* outside local network? if so, this can neither be a global broadcast nor
+ a subnet broadcast. */
+ if (!ip4_addr_netcmp(ipaddr, netif_ip4_addr(netif), netif_ip4_netmask(netif)) &&
+ !ip4_addr_islinklocal(ipaddr)) {
+#if LWIP_AUTOIP
+ struct ip_hdr *iphdr = LWIP_ALIGNMENT_CAST(struct ip_hdr*, q->payload);
+ /* According to RFC 3297, chapter 2.6.2 (Forwarding Rules), a packet with
+ a link-local source address must always be "directly to its destination
+ on the same physical link. The host MUST NOT send the packet to any
+ router for forwarding". */
+ if (!ip4_addr_islinklocal(&iphdr->src))
+#endif /* LWIP_AUTOIP */
+ {
+#ifdef LWIP_HOOK_ETHARP_GET_GW
+ /* For advanced routing, a single default gateway might not be enough, so get
+ the IP address of the gateway to handle the current destination address. */
+ dst_addr = LWIP_HOOK_ETHARP_GET_GW(netif, ipaddr);
+ if (dst_addr == NULL)
+#endif /* LWIP_HOOK_ETHARP_GET_GW */
+ {
+ /* interface has default gateway? */
+ if (!ip4_addr_isany_val(*netif_ip4_gw(netif))) {
+ /* send to hardware address of default gateway IP address */
+ dst_addr = netif_ip4_gw(netif);
+ /* no default gateway available */
+ } else {
+ /* no route to destination error (default gateway missing) */
+ return ERR_RTE;
+ }
+ }
+ }
+ }
+#if LWIP_NETIF_HWADDRHINT
+ if (netif->addr_hint != NULL) {
+ /* per-pcb cached entry was given */
+ u8_t etharp_cached_entry = *(netif->addr_hint);
+ if (etharp_cached_entry < ARP_TABLE_SIZE) {
+#endif /* LWIP_NETIF_HWADDRHINT */
+ if ((arp_table[etharp_cached_entry].state >= ETHARP_STATE_STABLE) &&
+#if ETHARP_TABLE_MATCH_NETIF
+ (arp_table[etharp_cached_entry].netif == netif) &&
+#endif
+ (ip4_addr_cmp(dst_addr, &arp_table[etharp_cached_entry].ipaddr))) {
+ /* the per-pcb-cached entry is stable and the right one! */
+ ETHARP_STATS_INC(etharp.cachehit);
+ return etharp_output_to_arp_index(netif, q, etharp_cached_entry);
+ }
+#if LWIP_NETIF_HWADDRHINT
+ }
+ }
+#endif /* LWIP_NETIF_HWADDRHINT */
+
+ /* find stable entry: do this here since this is a critical path for
+ throughput and etharp_find_entry() is kind of slow */
+ for (i = 0; i < ARP_TABLE_SIZE; i++) {
+ if ((arp_table[i].state >= ETHARP_STATE_STABLE) &&
+#if ETHARP_TABLE_MATCH_NETIF
+ (arp_table[i].netif == netif) &&
+#endif
+ (ip4_addr_cmp(dst_addr, &arp_table[i].ipaddr))) {
+ /* found an existing, stable entry */
+ ETHARP_SET_HINT(netif, i);
+ return etharp_output_to_arp_index(netif, q, i);
+ }
+ }
+ /* no stable entry found, use the (slower) query function:
+ queue on destination Ethernet address belonging to ipaddr */
+ return etharp_query(netif, dst_addr, q);
+ }
+
+ /* continuation for multicast/broadcast destinations */
+ /* obtain source Ethernet address of the given interface */
+ /* send packet directly on the link */
+ return ethernet_output(netif, q, (struct eth_addr*)(netif->hwaddr), dest, ETHTYPE_IP);
+}
+
+/**
+ * Send an ARP request for the given IP address and/or queue a packet.
+ *
+ * If the IP address was not yet in the cache, a pending ARP cache entry
+ * is added and an ARP request is sent for the given address. The packet
+ * is queued on this entry.
+ *
+ * If the IP address was already pending in the cache, a new ARP request
+ * is sent for the given address. The packet is queued on this entry.
+ *
+ * If the IP address was already stable in the cache, and a packet is
+ * given, it is directly sent and no ARP request is sent out.
+ *
+ * If the IP address was already stable in the cache, and no packet is
+ * given, an ARP request is sent out.
+ *
+ * @param netif The lwIP network interface on which ipaddr
+ * must be queried for.
+ * @param ipaddr The IP address to be resolved.
+ * @param q If non-NULL, a pbuf that must be delivered to the IP address.
+ * q is not freed by this function.
+ *
+ * @note q must only be ONE packet, not a packet queue!
+ *
+ * @return
+ * - ERR_BUF Could not make room for Ethernet header.
+ * - ERR_MEM Hardware address unknown, and no more ARP entries available
+ * to query for address or queue the packet.
+ * - ERR_MEM Could not queue packet due to memory shortage.
+ * - ERR_RTE No route to destination (no gateway to external networks).
+ * - ERR_ARG Non-unicast address given, those will not appear in ARP cache.
+ *
+ */
+err_t
+etharp_query(struct netif *netif, const ip4_addr_t *ipaddr, struct pbuf *q)
+{
+ struct eth_addr * srcaddr = (struct eth_addr *)netif->hwaddr;
+ err_t result = ERR_MEM;
+ int is_new_entry = 0;
+ s8_t i; /* ARP entry index */
+
+ /* non-unicast address? */
+ if (ip4_addr_isbroadcast(ipaddr, netif) ||
+ ip4_addr_ismulticast(ipaddr) ||
+ ip4_addr_isany(ipaddr)) {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: will not add non-unicast IP address to ARP cache\n"));
+ return ERR_ARG;
+ }
+
+ /* find entry in ARP cache, ask to create entry if queueing packet */
+ i = etharp_find_entry(ipaddr, ETHARP_FLAG_TRY_HARD, netif);
+
+ /* could not find or create entry? */
+ if (i < 0) {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: could not create ARP entry\n"));
+ if (q) {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: packet dropped\n"));
+ ETHARP_STATS_INC(etharp.memerr);
+ }
+ return (err_t)i;
+ }
+
+ /* mark a fresh entry as pending (we just sent a request) */
+ if (arp_table[i].state == ETHARP_STATE_EMPTY) {
+ is_new_entry = 1;
+ arp_table[i].state = ETHARP_STATE_PENDING;
+ /* record network interface for re-sending arp request in etharp_tmr */
+ arp_table[i].netif = netif;
+ }
+
+ /* { i is either a STABLE or (new or existing) PENDING entry } */
+ LWIP_ASSERT("arp_table[i].state == PENDING or STABLE",
+ ((arp_table[i].state == ETHARP_STATE_PENDING) ||
+ (arp_table[i].state >= ETHARP_STATE_STABLE)));
+
+ /* do we have a new entry? or an implicit query request? */
+ if (is_new_entry || (q == NULL)) {
+ /* try to resolve it; send out ARP request */
+ result = etharp_request(netif, ipaddr);
+ if (result != ERR_OK) {
+ /* ARP request couldn't be sent */
+ /* We don't re-send arp request in etharp_tmr, but we still queue packets,
+ since this failure could be temporary, and the next packet calling
+ etharp_query again could lead to sending the queued packets. */
+ }
+ if (q == NULL) {
+ return result;
+ }
+ }
+
+ /* packet given? */
+ LWIP_ASSERT("q != NULL", q != NULL);
+ /* stable entry? */
+ if (arp_table[i].state >= ETHARP_STATE_STABLE) {
+ /* we have a valid IP->Ethernet address mapping */
+ ETHARP_SET_HINT(netif, i);
+ /* send the packet */
+ result = ethernet_output(netif, q, srcaddr, &(arp_table[i].ethaddr), ETHTYPE_IP);
+ /* pending entry? (either just created or already pending */
+ } else if (arp_table[i].state == ETHARP_STATE_PENDING) {
+ /* entry is still pending, queue the given packet 'q' */
+ struct pbuf *p;
+ int copy_needed = 0;
+ /* IF q includes a PBUF_REF, PBUF_POOL or PBUF_RAM, we have no choice but
+ * to copy the whole queue into a new PBUF_RAM (see bug #11400)
+ * PBUF_ROMs can be left as they are, since ROM must not get changed. */
+ p = q;
+ while (p) {
+ LWIP_ASSERT("no packet queues allowed!", (p->len != p->tot_len) || (p->next == 0));
+ if (p->type != PBUF_ROM) {
+ copy_needed = 1;
+ break;
+ }
+ p = p->next;
+ }
+ if (copy_needed) {
+ /* copy the whole packet into new pbufs */
+ p = pbuf_alloc(PBUF_LINK, p->tot_len, PBUF_RAM);
+ if (p != NULL) {
+ if (pbuf_copy(p, q) != ERR_OK) {
+ pbuf_free(p);
+ p = NULL;
+ }
+ }
+ } else {
+ /* referencing the old pbuf is enough */
+ p = q;
+ pbuf_ref(p);
+ }
+ /* packet could be taken over? */
+ if (p != NULL) {
+ /* queue packet ... */
+#if ARP_QUEUEING
+ struct etharp_q_entry *new_entry;
+ /* allocate a new arp queue entry */
+ new_entry = (struct etharp_q_entry *)memp_malloc(MEMP_ARP_QUEUE);
+ if (new_entry != NULL) {
+ unsigned int qlen = 0;
+ new_entry->next = 0;
+ new_entry->p = p;
+ if (arp_table[i].q != NULL) {
+ /* queue was already existent, append the new entry to the end */
+ struct etharp_q_entry *r;
+ r = arp_table[i].q;
+ qlen++;
+ while (r->next != NULL) {
+ r = r->next;
+ qlen++;
+ }
+ r->next = new_entry;
+ } else {
+ /* queue did not exist, first item in queue */
+ arp_table[i].q = new_entry;
+ }
+#if ARP_QUEUE_LEN
+ if (qlen >= ARP_QUEUE_LEN) {
+ struct etharp_q_entry *old;
+ old = arp_table[i].q;
+ arp_table[i].q = arp_table[i].q->next;
+ pbuf_free(old->p);
+ memp_free(MEMP_ARP_QUEUE, old);
+ }
+#endif
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: queued packet %p on ARP entry %"S16_F"\n", (void *)q, (s16_t)i));
+ result = ERR_OK;
+ } else {
+ /* the pool MEMP_ARP_QUEUE is empty */
+ pbuf_free(p);
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: could not queue a copy of PBUF_REF packet %p (out of memory)\n", (void *)q));
+ result = ERR_MEM;
+ }
+#else /* ARP_QUEUEING */
+ /* always queue one packet per ARP request only, freeing a previously queued packet */
+ if (arp_table[i].q != NULL) {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: dropped previously queued packet %p for ARP entry %"S16_F"\n", (void *)q, (s16_t)i));
+ pbuf_free(arp_table[i].q);
+ }
+ arp_table[i].q = p;
+ result = ERR_OK;
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: queued packet %p on ARP entry %"S16_F"\n", (void *)q, (s16_t)i));
+#endif /* ARP_QUEUEING */
+ } else {
+ ETHARP_STATS_INC(etharp.memerr);
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: could not queue a copy of PBUF_REF packet %p (out of memory)\n", (void *)q));
+ result = ERR_MEM;
+ }
+ }
+ return result;
+}
+
+/**
+ * Send a raw ARP packet (opcode and all addresses can be modified)
+ *
+ * @param netif the lwip network interface on which to send the ARP packet
+ * @param ethsrc_addr the source MAC address for the ethernet header
+ * @param ethdst_addr the destination MAC address for the ethernet header
+ * @param hwsrc_addr the source MAC address for the ARP protocol header
+ * @param ipsrc_addr the source IP address for the ARP protocol header
+ * @param hwdst_addr the destination MAC address for the ARP protocol header
+ * @param ipdst_addr the destination IP address for the ARP protocol header
+ * @param opcode the type of the ARP packet
+ * @return ERR_OK if the ARP packet has been sent
+ * ERR_MEM if the ARP packet couldn't be allocated
+ * any other err_t on failure
+ */
+static err_t
+etharp_raw(struct netif *netif, const struct eth_addr *ethsrc_addr,
+ const struct eth_addr *ethdst_addr,
+ const struct eth_addr *hwsrc_addr, const ip4_addr_t *ipsrc_addr,
+ const struct eth_addr *hwdst_addr, const ip4_addr_t *ipdst_addr,
+ const u16_t opcode)
+{
+ struct pbuf *p;
+ err_t result = ERR_OK;
+ struct etharp_hdr *hdr;
+
+ LWIP_ASSERT("netif != NULL", netif != NULL);
+
+ /* allocate a pbuf for the outgoing ARP request packet */
+ p = pbuf_alloc(PBUF_LINK, SIZEOF_ETHARP_HDR, PBUF_RAM);
+ /* could allocate a pbuf for an ARP request? */
+ if (p == NULL) {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
+ ("etharp_raw: could not allocate pbuf for ARP request.\n"));
+ ETHARP_STATS_INC(etharp.memerr);
+ return ERR_MEM;
+ }
+ LWIP_ASSERT("check that first pbuf can hold struct etharp_hdr",
+ (p->len >= SIZEOF_ETHARP_HDR));
+
+ hdr = (struct etharp_hdr *)p->payload;
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_raw: sending raw ARP packet.\n"));
+ hdr->opcode = lwip_htons(opcode);
+
+ LWIP_ASSERT("netif->hwaddr_len must be the same as ETH_HWADDR_LEN for etharp!",
+ (netif->hwaddr_len == ETH_HWADDR_LEN));
+
+ /* Write the ARP MAC-Addresses */
+ ETHADDR16_COPY(&hdr->shwaddr, hwsrc_addr);
+ ETHADDR16_COPY(&hdr->dhwaddr, hwdst_addr);
+ /* Copy struct ip4_addr2 to aligned ip4_addr, to support compilers without
+ * structure packing. */
+ IPADDR2_COPY(&hdr->sipaddr, ipsrc_addr);
+ IPADDR2_COPY(&hdr->dipaddr, ipdst_addr);
+
+ hdr->hwtype = PP_HTONS(HWTYPE_ETHERNET);
+ hdr->proto = PP_HTONS(ETHTYPE_IP);
+ /* set hwlen and protolen */
+ hdr->hwlen = ETH_HWADDR_LEN;
+ hdr->protolen = sizeof(ip4_addr_t);
+
+ /* send ARP query */
+#if LWIP_AUTOIP
+ /* If we are using Link-Local, all ARP packets that contain a Link-Local
+ * 'sender IP address' MUST be sent using link-layer broadcast instead of
+ * link-layer unicast. (See RFC3927 Section 2.5, last paragraph) */
+ if(ip4_addr_islinklocal(ipsrc_addr)) {
+ ethernet_output(netif, p, ethsrc_addr, &ethbroadcast, ETHTYPE_ARP);
+ } else
+#endif /* LWIP_AUTOIP */
+ {
+ ethernet_output(netif, p, ethsrc_addr, ethdst_addr, ETHTYPE_ARP);
+ }
+
+ ETHARP_STATS_INC(etharp.xmit);
+ /* free ARP query packet */
+ pbuf_free(p);
+ p = NULL;
+ /* could not allocate pbuf for ARP request */
+
+ return result;
+}
+
+/**
+ * Send an ARP request packet asking for ipaddr to a specific eth address.
+ * Used to send unicast request to refresh the ARP table just before an entry
+ * times out
+ *
+ * @param netif the lwip network interface on which to send the request
+ * @param ipaddr the IP address for which to ask
+ * @param hw_dst_addr the ethernet address to send this packet to
+ * @return ERR_OK if the request has been sent
+ * ERR_MEM if the ARP packet couldn't be allocated
+ * any other err_t on failure
+ */
+static err_t
+etharp_request_dst(struct netif *netif, const ip4_addr_t *ipaddr, const struct eth_addr* hw_dst_addr)
+{
+ return etharp_raw(netif, (struct eth_addr *)netif->hwaddr, hw_dst_addr,
+ (struct eth_addr *)netif->hwaddr, netif_ip4_addr(netif), &ethzero,
+ ipaddr, ARP_REQUEST);
+}
+
+/**
+ * Send an ARP request packet asking for ipaddr.
+ *
+ * @param netif the lwip network interface on which to send the request
+ * @param ipaddr the IP address for which to ask
+ * @return ERR_OK if the request has been sent
+ * ERR_MEM if the ARP packet couldn't be allocated
+ * any other err_t on failure
+ */
+err_t
+etharp_request(struct netif *netif, const ip4_addr_t *ipaddr)
+{
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_request: sending ARP request.\n"));
+ return etharp_request_dst(netif, ipaddr, &ethbroadcast);
+}
+#endif /* LWIP_IPV4 && LWIP_ARP */
+
+#endif /* LWIP_ARP || LWIP_ETHERNET */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/ipv4/icmp.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/ipv4/icmp.c
new file mode 100644
index 0000000..5ee24ee
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/ipv4/icmp.c
@@ -0,0 +1,397 @@
+/**
+ * @file
+ * ICMP - Internet Control Message Protocol
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+/* Some ICMP messages should be passed to the transport protocols. This
+ is not implemented. */
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV4 && LWIP_ICMP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/icmp.h"
+#include "lwip/inet_chksum.h"
+#include "lwip/ip.h"
+#include "lwip/def.h"
+#include "lwip/stats.h"
+
+#include <string.h>
+
+#ifdef LWIP_HOOK_FILENAME
+#include LWIP_HOOK_FILENAME
+#endif
+
+/** Small optimization: set to 0 if incoming PBUF_POOL pbuf always can be
+ * used to modify and send a response packet (and to 1 if this is not the case,
+ * e.g. when link header is stripped of when receiving) */
+#ifndef LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN
+#define LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN 1
+#endif /* LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN */
+
+/* The amount of data from the original packet to return in a dest-unreachable */
+#define ICMP_DEST_UNREACH_DATASIZE 8
+
+static void icmp_send_response(struct pbuf *p, u8_t type, u8_t code);
+
+/**
+ * Processes ICMP input packets, called from ip_input().
+ *
+ * Currently only processes icmp echo requests and sends
+ * out the echo response.
+ *
+ * @param p the icmp echo request packet, p->payload pointing to the icmp header
+ * @param inp the netif on which this packet was received
+ */
+void
+icmp_input(struct pbuf *p, struct netif *inp)
+{
+ u8_t type;
+#ifdef LWIP_DEBUG
+ u8_t code;
+#endif /* LWIP_DEBUG */
+ struct icmp_echo_hdr *iecho;
+ const struct ip_hdr *iphdr_in;
+ u16_t hlen;
+ const ip4_addr_t* src;
+
+ ICMP_STATS_INC(icmp.recv);
+ MIB2_STATS_INC(mib2.icmpinmsgs);
+
+ iphdr_in = ip4_current_header();
+ hlen = IPH_HL(iphdr_in) * 4;
+ if (hlen < IP_HLEN) {
+ LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: short IP header (%"S16_F" bytes) received\n", hlen));
+ goto lenerr;
+ }
+ if (p->len < sizeof(u16_t)*2) {
+ LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: short ICMP (%"U16_F" bytes) received\n", p->tot_len));
+ goto lenerr;
+ }
+
+ type = *((u8_t *)p->payload);
+#ifdef LWIP_DEBUG
+ code = *(((u8_t *)p->payload)+1);
+#endif /* LWIP_DEBUG */
+ switch (type) {
+ case ICMP_ER:
+ /* This is OK, echo reply might have been parsed by a raw PCB
+ (as obviously, an echo request has been sent, too). */
+ MIB2_STATS_INC(mib2.icmpinechoreps);
+ break;
+ case ICMP_ECHO:
+ MIB2_STATS_INC(mib2.icmpinechos);
+ src = ip4_current_dest_addr();
+ /* multicast destination address? */
+ if (ip4_addr_ismulticast(ip4_current_dest_addr())) {
+#if LWIP_MULTICAST_PING
+ /* For multicast, use address of receiving interface as source address */
+ src = netif_ip4_addr(inp);
+#else /* LWIP_MULTICAST_PING */
+ LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: Not echoing to multicast pings\n"));
+ goto icmperr;
+#endif /* LWIP_MULTICAST_PING */
+ }
+ /* broadcast destination address? */
+ if (ip4_addr_isbroadcast(ip4_current_dest_addr(), ip_current_netif())) {
+#if LWIP_BROADCAST_PING
+ /* For broadcast, use address of receiving interface as source address */
+ src = netif_ip4_addr(inp);
+#else /* LWIP_BROADCAST_PING */
+ LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: Not echoing to broadcast pings\n"));
+ goto icmperr;
+#endif /* LWIP_BROADCAST_PING */
+ }
+ LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ping\n"));
+ if (p->tot_len < sizeof(struct icmp_echo_hdr)) {
+ LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: bad ICMP echo received\n"));
+ goto lenerr;
+ }
+#if CHECKSUM_CHECK_ICMP
+ IF__NETIF_CHECKSUM_ENABLED(inp, NETIF_CHECKSUM_CHECK_ICMP) {
+ if (inet_chksum_pbuf(p) != 0) {
+ LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: checksum failed for received ICMP echo\n"));
+ pbuf_free(p);
+ ICMP_STATS_INC(icmp.chkerr);
+ MIB2_STATS_INC(mib2.icmpinerrors);
+ return;
+ }
+ }
+#endif
+#if LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN
+ if (pbuf_header(p, (s16_t)(hlen + PBUF_LINK_HLEN + PBUF_LINK_ENCAPSULATION_HLEN))) {
+ /* p is not big enough to contain link headers
+ * allocate a new one and copy p into it
+ */
+ struct pbuf *r;
+ /* allocate new packet buffer with space for link headers */
+ r = pbuf_alloc(PBUF_LINK, p->tot_len + hlen, PBUF_RAM);
+ if (r == NULL) {
+ LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: allocating new pbuf failed\n"));
+ goto icmperr;
+ }
+ if (r->len < hlen + sizeof(struct icmp_echo_hdr)) {
+ LWIP_DEBUGF(ICMP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("first pbuf cannot hold the ICMP header"));
+ pbuf_free(r);
+ goto icmperr;
+ }
+ /* copy the ip header */
+ MEMCPY(r->payload, iphdr_in, hlen);
+ /* switch r->payload back to icmp header (cannot fail) */
+ if (pbuf_header(r, (s16_t)-hlen)) {
+ LWIP_ASSERT("icmp_input: moving r->payload to icmp header failed\n", 0);
+ pbuf_free(r);
+ goto icmperr;
+ }
+ /* copy the rest of the packet without ip header */
+ if (pbuf_copy(r, p) != ERR_OK) {
+ LWIP_DEBUGF(ICMP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("icmp_input: copying to new pbuf failed"));
+ pbuf_free(r);
+ goto icmperr;
+ }
+ /* free the original p */
+ pbuf_free(p);
+ /* we now have an identical copy of p that has room for link headers */
+ p = r;
+ } else {
+ /* restore p->payload to point to icmp header (cannot fail) */
+ if (pbuf_header(p, -(s16_t)(hlen + PBUF_LINK_HLEN + PBUF_LINK_ENCAPSULATION_HLEN))) {
+ LWIP_ASSERT("icmp_input: restoring original p->payload failed\n", 0);
+ goto icmperr;
+ }
+ }
+#endif /* LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN */
+ /* At this point, all checks are OK. */
+ /* We generate an answer by switching the dest and src ip addresses,
+ * setting the icmp type to ECHO_RESPONSE and updating the checksum. */
+ iecho = (struct icmp_echo_hdr *)p->payload;
+ if (pbuf_header(p, (s16_t)hlen)) {
+ LWIP_DEBUGF(ICMP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("Can't move over header in packet"));
+ } else {
+ err_t ret;
+ struct ip_hdr *iphdr = (struct ip_hdr*)p->payload;
+ ip4_addr_copy(iphdr->src, *src);
+ ip4_addr_copy(iphdr->dest, *ip4_current_src_addr());
+ ICMPH_TYPE_SET(iecho, ICMP_ER);
+#if CHECKSUM_GEN_ICMP
+ IF__NETIF_CHECKSUM_ENABLED(inp, NETIF_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);
+ }
+ }
+#if LWIP_CHECKSUM_CTRL_PER_NETIF
+ else {
+ iecho->chksum = 0;
+ }
+#endif /* LWIP_CHECKSUM_CTRL_PER_NETIF */
+#else /* CHECKSUM_GEN_ICMP */
+ iecho->chksum = 0;
+#endif /* CHECKSUM_GEN_ICMP */
+
+ /* Set the correct TTL and recalculate the header checksum. */
+ IPH_TTL_SET(iphdr, ICMP_TTL);
+ IPH_CHKSUM_SET(iphdr, 0);
+#if CHECKSUM_GEN_IP
+ IF__NETIF_CHECKSUM_ENABLED(inp, NETIF_CHECKSUM_GEN_IP) {
+ IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, hlen));
+ }
+#endif /* CHECKSUM_GEN_IP */
+
+ ICMP_STATS_INC(icmp.xmit);
+ /* increase number of messages attempted to send */
+ MIB2_STATS_INC(mib2.icmpoutmsgs);
+ /* increase number of echo replies attempted to send */
+ MIB2_STATS_INC(mib2.icmpoutechoreps);
+
+ /* send an ICMP packet */
+ ret = ip4_output_if(p, src, LWIP_IP_HDRINCL,
+ ICMP_TTL, 0, IP_PROTO_ICMP, inp);
+ if (ret != ERR_OK) {
+ LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ip_output_if returned an error: %s\n", lwip_strerr(ret)));
+ }
+ }
+ break;
+ default:
+ if (type == ICMP_DUR) {
+ MIB2_STATS_INC(mib2.icmpindestunreachs);
+ } else if (type == ICMP_TE) {
+ MIB2_STATS_INC(mib2.icmpintimeexcds);
+ } else if (type == ICMP_PP) {
+ MIB2_STATS_INC(mib2.icmpinparmprobs);
+ } else if (type == ICMP_SQ) {
+ MIB2_STATS_INC(mib2.icmpinsrcquenchs);
+ } else if (type == ICMP_RD) {
+ MIB2_STATS_INC(mib2.icmpinredirects);
+ } else if (type == ICMP_TS) {
+ MIB2_STATS_INC(mib2.icmpintimestamps);
+ } else if (type == ICMP_TSR) {
+ MIB2_STATS_INC(mib2.icmpintimestampreps);
+ } else if (type == ICMP_AM) {
+ MIB2_STATS_INC(mib2.icmpinaddrmasks);
+ } else if (type == ICMP_AMR) {
+ MIB2_STATS_INC(mib2.icmpinaddrmaskreps);
+ }
+ LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ICMP type %"S16_F" code %"S16_F" not supported.\n",
+ (s16_t)type, (s16_t)code));
+ ICMP_STATS_INC(icmp.proterr);
+ ICMP_STATS_INC(icmp.drop);
+ }
+ pbuf_free(p);
+ return;
+lenerr:
+ pbuf_free(p);
+ ICMP_STATS_INC(icmp.lenerr);
+ MIB2_STATS_INC(mib2.icmpinerrors);
+ return;
+#if LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN || !LWIP_MULTICAST_PING || !LWIP_BROADCAST_PING
+icmperr:
+ pbuf_free(p);
+ ICMP_STATS_INC(icmp.err);
+ MIB2_STATS_INC(mib2.icmpinerrors);
+ return;
+#endif /* LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN || !LWIP_MULTICAST_PING || !LWIP_BROADCAST_PING */
+}
+
+/**
+ * Send an icmp 'destination unreachable' packet, called from ip_input() if
+ * the transport layer protocol is unknown and from udp_input() if the local
+ * port is not bound.
+ *
+ * @param p the input packet for which the 'unreachable' should be sent,
+ * p->payload pointing to the IP header
+ * @param t type of the 'unreachable' packet
+ */
+void
+icmp_dest_unreach(struct pbuf *p, enum icmp_dur_type t)
+{
+ MIB2_STATS_INC(mib2.icmpoutdestunreachs);
+ icmp_send_response(p, ICMP_DUR, t);
+}
+
+#if IP_FORWARD || IP_REASSEMBLY
+/**
+ * Send a 'time exceeded' packet, called from ip_forward() if TTL is 0.
+ *
+ * @param p the input packet for which the 'time exceeded' should be sent,
+ * p->payload pointing to the IP header
+ * @param t type of the 'time exceeded' packet
+ */
+void
+icmp_time_exceeded(struct pbuf *p, enum icmp_te_type t)
+{
+ MIB2_STATS_INC(mib2.icmpouttimeexcds);
+ icmp_send_response(p, ICMP_TE, t);
+}
+
+#endif /* IP_FORWARD || IP_REASSEMBLY */
+
+/**
+ * Send an icmp packet in response to an incoming packet.
+ *
+ * @param p the input packet for which the 'unreachable' should be sent,
+ * p->payload pointing to the IP header
+ * @param type Type of the ICMP header
+ * @param code Code of the ICMP header
+ */
+static void
+icmp_send_response(struct pbuf *p, u8_t type, u8_t code)
+{
+ struct pbuf *q;
+ struct ip_hdr *iphdr;
+ /* we can use the echo header here */
+ struct icmp_echo_hdr *icmphdr;
+ ip4_addr_t iphdr_src;
+ struct netif *netif;
+
+ /* increase number of messages attempted to send */
+ MIB2_STATS_INC(mib2.icmpoutmsgs);
+
+ /* ICMP header + IP header + 8 bytes of data */
+ q = pbuf_alloc(PBUF_IP, sizeof(struct icmp_echo_hdr) + IP_HLEN + ICMP_DEST_UNREACH_DATASIZE,
+ PBUF_RAM);
+ if (q == NULL) {
+ LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded: failed to allocate pbuf for ICMP packet.\n"));
+ MIB2_STATS_INC(mib2.icmpouterrors);
+ return;
+ }
+ LWIP_ASSERT("check that first pbuf can hold icmp message",
+ (q->len >= (sizeof(struct icmp_echo_hdr) + IP_HLEN + ICMP_DEST_UNREACH_DATASIZE)));
+
+ iphdr = (struct ip_hdr *)p->payload;
+ LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded from "));
+ ip4_addr_debug_print_val(ICMP_DEBUG, iphdr->src);
+ LWIP_DEBUGF(ICMP_DEBUG, (" to "));
+ ip4_addr_debug_print_val(ICMP_DEBUG, iphdr->dest);
+ LWIP_DEBUGF(ICMP_DEBUG, ("\n"));
+
+ icmphdr = (struct icmp_echo_hdr *)q->payload;
+ icmphdr->type = type;
+ icmphdr->code = code;
+ icmphdr->id = 0;
+ icmphdr->seqno = 0;
+
+ /* copy fields from original packet */
+ SMEMCPY((u8_t *)q->payload + sizeof(struct icmp_echo_hdr), (u8_t *)p->payload,
+ IP_HLEN + ICMP_DEST_UNREACH_DATASIZE);
+
+ ip4_addr_copy(iphdr_src, iphdr->src);
+#ifdef LWIP_HOOK_IP4_ROUTE_SRC
+ {
+ ip4_addr_t iphdr_dst;
+ ip4_addr_copy(iphdr_dst, iphdr->dest);
+ netif = ip4_route_src(&iphdr_src, &iphdr_dst);
+ }
+#else
+ netif = ip4_route(&iphdr_src);
+#endif
+ if (netif != NULL) {
+ /* calculate checksum */
+ icmphdr->chksum = 0;
+#if CHECKSUM_GEN_ICMP
+ IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_ICMP) {
+ icmphdr->chksum = inet_chksum(icmphdr, q->len);
+ }
+#endif
+ ICMP_STATS_INC(icmp.xmit);
+ ip4_output_if(q, NULL, &iphdr_src, ICMP_TTL, 0, IP_PROTO_ICMP, netif);
+ }
+ pbuf_free(q);
+}
+
+#endif /* LWIP_IPV4 && LWIP_ICMP */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/ipv4/igmp.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/ipv4/igmp.c
new file mode 100644
index 0000000..74a6c37
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/ipv4/igmp.c
@@ -0,0 +1,800 @@
+/**
+ * @file
+ * IGMP - Internet Group Management Protocol
+ *
+ * @defgroup igmp IGMP
+ * @ingroup ip4
+ * To be called from TCPIP thread
+ */
+
+/*
+ * Copyright (c) 2002 CITEL Technologies Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of CITEL Technologies Ltd nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY CITEL TECHNOLOGIES AND CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL CITEL TECHNOLOGIES OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This file is a contribution to the lwIP TCP/IP stack.
+ * The Swedish Institute of Computer Science and Adam Dunkels
+ * are specifically granted permission to redistribute this
+ * source code.
+*/
+
+/*-------------------------------------------------------------
+Note 1)
+Although the rfc requires V1 AND V2 capability
+we will only support v2 since now V1 is very old (August 1989)
+V1 can be added if required
+
+a debug print and statistic have been implemented to
+show this up.
+-------------------------------------------------------------
+-------------------------------------------------------------
+Note 2)
+A query for a specific group address (as opposed to ALLHOSTS)
+has now been implemented as I am unsure if it is required
+
+a debug print and statistic have been implemented to
+show this up.
+-------------------------------------------------------------
+-------------------------------------------------------------
+Note 3)
+The router alert rfc 2113 is implemented in outgoing packets
+but not checked rigorously incoming
+-------------------------------------------------------------
+Steve Reynolds
+------------------------------------------------------------*/
+
+/*-----------------------------------------------------------------------------
+ * RFC 988 - Host extensions for IP multicasting - V0
+ * RFC 1054 - Host extensions for IP multicasting -
+ * RFC 1112 - Host extensions for IP multicasting - V1
+ * RFC 2236 - Internet Group Management Protocol, Version 2 - V2 <- this code is based on this RFC (it's the "de facto" standard)
+ * RFC 3376 - Internet Group Management Protocol, Version 3 - V3
+ * RFC 4604 - Using Internet Group Management Protocol Version 3... - V3+
+ * RFC 2113 - IP Router Alert Option -
+ *----------------------------------------------------------------------------*/
+
+/*-----------------------------------------------------------------------------
+ * Includes
+ *----------------------------------------------------------------------------*/
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV4 && LWIP_IGMP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/igmp.h"
+#include "lwip/debug.h"
+#include "lwip/def.h"
+#include "lwip/mem.h"
+#include "lwip/ip.h"
+#include "lwip/inet_chksum.h"
+#include "lwip/netif.h"
+#include "lwip/stats.h"
+#include "lwip/prot/igmp.h"
+
+#include "string.h"
+
+static struct igmp_group *igmp_lookup_group(struct netif *ifp, const ip4_addr_t *addr);
+static err_t igmp_remove_group(struct netif* netif, struct igmp_group *group);
+static void igmp_timeout(struct netif *netif, struct igmp_group *group);
+static void igmp_start_timer(struct igmp_group *group, u8_t max_time);
+static void igmp_delaying_member(struct igmp_group *group, u8_t maxresp);
+static err_t igmp_ip_output_if(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest, struct netif *netif);
+static void igmp_send(struct netif *netif, struct igmp_group *group, u8_t type);
+
+static ip4_addr_t allsystems;
+static ip4_addr_t allrouters;
+
+/**
+ * Initialize the IGMP module
+ */
+void
+igmp_init(void)
+{
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_init: initializing\n"));
+
+ IP4_ADDR(&allsystems, 224, 0, 0, 1);
+ IP4_ADDR(&allrouters, 224, 0, 0, 2);
+}
+
+/**
+ * Start IGMP processing on interface
+ *
+ * @param netif network interface on which start IGMP processing
+ */
+err_t
+igmp_start(struct netif *netif)
+{
+ struct igmp_group* group;
+
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_start: starting IGMP processing on if %p\n", (void*)netif));
+
+ group = igmp_lookup_group(netif, &allsystems);
+
+ if (group != NULL) {
+ group->group_state = IGMP_GROUP_IDLE_MEMBER;
+ group->use++;
+
+ /* Allow the igmp messages at the MAC level */
+ if (netif->igmp_mac_filter != NULL) {
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_start: igmp_mac_filter(ADD "));
+ ip4_addr_debug_print_val(IGMP_DEBUG, allsystems);
+ LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", (void*)netif));
+ netif->igmp_mac_filter(netif, &allsystems, NETIF_ADD_MAC_FILTER);
+ }
+
+ return ERR_OK;
+ }
+
+ return ERR_MEM;
+}
+
+/**
+ * Stop IGMP processing on interface
+ *
+ * @param netif network interface on which stop IGMP processing
+ */
+err_t
+igmp_stop(struct netif *netif)
+{
+ struct igmp_group *group = netif_igmp_data(netif);
+
+ netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_IGMP, NULL);
+
+ while (group != NULL) {
+ struct igmp_group *next = group->next; /* avoid use-after-free below */
+
+ /* disable the group at the MAC level */
+ if (netif->igmp_mac_filter != NULL) {
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_stop: igmp_mac_filter(DEL "));
+ ip4_addr_debug_print(IGMP_DEBUG, &group->group_address);
+ LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", (void*)netif));
+ netif->igmp_mac_filter(netif, &(group->group_address), NETIF_DEL_MAC_FILTER);
+ }
+
+ /* free group */
+ memp_free(MEMP_IGMP_GROUP, group);
+
+ /* move to "next" */
+ group = next;
+ }
+ return ERR_OK;
+}
+
+/**
+ * Report IGMP memberships for this interface
+ *
+ * @param netif network interface on which report IGMP memberships
+ */
+void
+igmp_report_groups(struct netif *netif)
+{
+ struct igmp_group *group = netif_igmp_data(netif);
+
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_report_groups: sending IGMP reports on if %p\n", (void*)netif));
+
+ /* Skip the first group in the list, it is always the allsystems group added in igmp_start() */
+ if(group != NULL) {
+ group = group->next;
+ }
+
+ while (group != NULL) {
+ igmp_delaying_member(group, IGMP_JOIN_DELAYING_MEMBER_TMR);
+ group = group->next;
+ }
+}
+
+/**
+ * Search for a group in the global igmp_group_list
+ *
+ * @param ifp the network interface for which to look
+ * @param addr the group ip address to search for
+ * @return a struct igmp_group* if the group has been found,
+ * NULL if the group wasn't found.
+ */
+struct igmp_group *
+igmp_lookfor_group(struct netif *ifp, const ip4_addr_t *addr)
+{
+ struct igmp_group *group = netif_igmp_data(ifp);
+
+ while (group != NULL) {
+ if (ip4_addr_cmp(&(group->group_address), addr)) {
+ return group;
+ }
+ group = group->next;
+ }
+
+ /* to be clearer, we return NULL here instead of
+ * 'group' (which is also NULL at this point).
+ */
+ return NULL;
+}
+
+/**
+ * Search for a specific igmp group and create a new one if not found-
+ *
+ * @param ifp the network interface for which to look
+ * @param addr the group ip address to search
+ * @return a struct igmp_group*,
+ * NULL on memory error.
+ */
+static struct igmp_group *
+igmp_lookup_group(struct netif *ifp, const ip4_addr_t *addr)
+{
+ struct igmp_group *group;
+ struct igmp_group *list_head = netif_igmp_data(ifp);
+
+ /* Search if the group already exists */
+ group = igmp_lookfor_group(ifp, addr);
+ if (group != NULL) {
+ /* Group already exists. */
+ return group;
+ }
+
+ /* Group doesn't exist yet, create a new one */
+ group = (struct igmp_group *)memp_malloc(MEMP_IGMP_GROUP);
+ if (group != NULL) {
+ ip4_addr_set(&(group->group_address), addr);
+ group->timer = 0; /* Not running */
+ group->group_state = IGMP_GROUP_NON_MEMBER;
+ group->last_reporter_flag = 0;
+ group->use = 0;
+
+ /* Ensure allsystems group is always first in list */
+ if (list_head == NULL) {
+ /* this is the first entry in linked list */
+ LWIP_ASSERT("igmp_lookup_group: first group must be allsystems",
+ (ip4_addr_cmp(addr, &allsystems) != 0));
+ group->next = NULL;
+ netif_set_client_data(ifp, LWIP_NETIF_CLIENT_DATA_INDEX_IGMP, group);
+ } else {
+ /* append _after_ first entry */
+ LWIP_ASSERT("igmp_lookup_group: all except first group must not be allsystems",
+ (ip4_addr_cmp(addr, &allsystems) == 0));
+ group->next = list_head->next;
+ list_head->next = group;
+ }
+ }
+
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_lookup_group: %sallocated a new group with address ", (group?"":"impossible to ")));
+ ip4_addr_debug_print(IGMP_DEBUG, addr);
+ LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", (void*)ifp));
+
+ return group;
+}
+
+/**
+ * Remove a group in the global igmp_group_list, but don't free it yet
+ *
+ * @param group the group to remove from the global igmp_group_list
+ * @return ERR_OK if group was removed from the list, an err_t otherwise
+ */
+static err_t
+igmp_remove_group(struct netif* netif, struct igmp_group *group)
+{
+ err_t err = ERR_OK;
+ struct igmp_group *tmp_group;
+
+ /* Skip the first group in the list, it is always the allsystems group added in igmp_start() */
+ for (tmp_group = netif_igmp_data(netif); tmp_group != NULL; tmp_group = tmp_group->next) {
+ if (tmp_group->next == group) {
+ tmp_group->next = group->next;
+ break;
+ }
+ }
+ /* Group not found in the global igmp_group_list */
+ if (tmp_group == NULL) {
+ err = ERR_ARG;
+ }
+
+ return err;
+}
+
+/**
+ * Called from ip_input() if a new IGMP packet is received.
+ *
+ * @param p received igmp packet, p->payload pointing to the igmp header
+ * @param inp network interface on which the packet was received
+ * @param dest destination ip address of the igmp packet
+ */
+void
+igmp_input(struct pbuf *p, struct netif *inp, const ip4_addr_t *dest)
+{
+ struct igmp_msg* igmp;
+ struct igmp_group* group;
+ struct igmp_group* groupref;
+
+ IGMP_STATS_INC(igmp.recv);
+
+ /* Note that the length CAN be greater than 8 but only 8 are used - All are included in the checksum */
+ if (p->len < IGMP_MINLEN) {
+ pbuf_free(p);
+ IGMP_STATS_INC(igmp.lenerr);
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: length error\n"));
+ return;
+ }
+
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: message from "));
+ ip4_addr_debug_print(IGMP_DEBUG, &(ip4_current_header()->src));
+ LWIP_DEBUGF(IGMP_DEBUG, (" to address "));
+ ip4_addr_debug_print(IGMP_DEBUG, &(ip4_current_header()->dest));
+ LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", (void*)inp));
+
+ /* Now calculate and check the checksum */
+ igmp = (struct igmp_msg *)p->payload;
+ if (inet_chksum(igmp, p->len)) {
+ pbuf_free(p);
+ IGMP_STATS_INC(igmp.chkerr);
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: checksum error\n"));
+ return;
+ }
+
+ /* Packet is ok so find an existing group */
+ group = igmp_lookfor_group(inp, dest); /* use the destination IP address of incoming packet */
+
+ /* If group can be found or create... */
+ if (!group) {
+ pbuf_free(p);
+ IGMP_STATS_INC(igmp.drop);
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP frame not for us\n"));
+ return;
+ }
+
+ /* NOW ACT ON THE INCOMING MESSAGE TYPE... */
+ switch (igmp->igmp_msgtype) {
+ case IGMP_MEMB_QUERY:
+ /* IGMP_MEMB_QUERY to the "all systems" address ? */
+ if ((ip4_addr_cmp(dest, &allsystems)) && ip4_addr_isany(&igmp->igmp_group_address)) {
+ /* THIS IS THE GENERAL QUERY */
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: General IGMP_MEMB_QUERY on \"ALL SYSTEMS\" address (224.0.0.1) [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp)));
+
+ if (igmp->igmp_maxresp == 0) {
+ IGMP_STATS_INC(igmp.rx_v1);
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: got an all hosts query with time== 0 - this is V1 and not implemented - treat as v2\n"));
+ igmp->igmp_maxresp = IGMP_V1_DELAYING_MEMBER_TMR;
+ } else {
+ IGMP_STATS_INC(igmp.rx_general);
+ }
+
+ groupref = netif_igmp_data(inp);
+
+ /* Do not send messages on the all systems group address! */
+ /* Skip the first group in the list, it is always the allsystems group added in igmp_start() */
+ if(groupref != NULL) {
+ groupref = groupref->next;
+ }
+
+ while (groupref) {
+ igmp_delaying_member(groupref, igmp->igmp_maxresp);
+ groupref = groupref->next;
+ }
+ } else {
+ /* IGMP_MEMB_QUERY to a specific group ? */
+ if (!ip4_addr_isany(&igmp->igmp_group_address)) {
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP_MEMB_QUERY to a specific group "));
+ ip4_addr_debug_print(IGMP_DEBUG, &igmp->igmp_group_address);
+ if (ip4_addr_cmp(dest, &allsystems)) {
+ ip4_addr_t groupaddr;
+ LWIP_DEBUGF(IGMP_DEBUG, (" using \"ALL SYSTEMS\" address (224.0.0.1) [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp)));
+ /* we first need to re-look for the group since we used dest last time */
+ ip4_addr_copy(groupaddr, igmp->igmp_group_address);
+ group = igmp_lookfor_group(inp, &groupaddr);
+ } else {
+ LWIP_DEBUGF(IGMP_DEBUG, (" with the group address as destination [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp)));
+ }
+
+ if (group != NULL) {
+ IGMP_STATS_INC(igmp.rx_group);
+ igmp_delaying_member(group, igmp->igmp_maxresp);
+ } else {
+ IGMP_STATS_INC(igmp.drop);
+ }
+ } else {
+ IGMP_STATS_INC(igmp.proterr);
+ }
+ }
+ break;
+ case IGMP_V2_MEMB_REPORT:
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP_V2_MEMB_REPORT\n"));
+ IGMP_STATS_INC(igmp.rx_report);
+ if (group->group_state == IGMP_GROUP_DELAYING_MEMBER) {
+ /* This is on a specific group we have already looked up */
+ group->timer = 0; /* stopped */
+ group->group_state = IGMP_GROUP_IDLE_MEMBER;
+ group->last_reporter_flag = 0;
+ }
+ break;
+ default:
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: unexpected msg %d in state %d on group %p on if %p\n",
+ igmp->igmp_msgtype, group->group_state, (void*)&group, (void*)inp));
+ IGMP_STATS_INC(igmp.proterr);
+ break;
+ }
+
+ pbuf_free(p);
+ return;
+}
+
+/**
+ * @ingroup igmp
+ * Join a group on one network interface.
+ *
+ * @param ifaddr ip address of the network interface which should join a new group
+ * @param groupaddr the ip address of the group which to join
+ * @return ERR_OK if group was joined on the netif(s), an err_t otherwise
+ */
+err_t
+igmp_joingroup(const ip4_addr_t *ifaddr, const ip4_addr_t *groupaddr)
+{
+ err_t err = ERR_VAL; /* no matching interface */
+ struct netif *netif;
+
+ /* make sure it is multicast address */
+ LWIP_ERROR("igmp_joingroup: attempt to join non-multicast address", ip4_addr_ismulticast(groupaddr), return ERR_VAL;);
+ LWIP_ERROR("igmp_joingroup: attempt to join allsystems address", (!ip4_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;);
+
+ /* loop through netif's */
+ netif = netif_list;
+ while (netif != NULL) {
+ /* Should we join this interface ? */
+ if ((netif->flags & NETIF_FLAG_IGMP) && ((ip4_addr_isany(ifaddr) || ip4_addr_cmp(netif_ip4_addr(netif), ifaddr)))) {
+ err = igmp_joingroup_netif(netif, groupaddr);
+ if (err != ERR_OK) {
+ /* Return an error even if some network interfaces are joined */
+ /** @todo undo any other netif already joined */
+ return err;
+ }
+ }
+ /* proceed to next network interface */
+ netif = netif->next;
+ }
+
+ return err;
+}
+
+/**
+ * @ingroup igmp
+ * Join a group on one network interface.
+ *
+ * @param netif the network interface which should join a new group
+ * @param groupaddr the ip address of the group which to join
+ * @return ERR_OK if group was joined on the netif, an err_t otherwise
+ */
+err_t
+igmp_joingroup_netif(struct netif *netif, const ip4_addr_t *groupaddr)
+{
+ struct igmp_group *group;
+
+ /* make sure it is multicast address */
+ LWIP_ERROR("igmp_joingroup_netif: attempt to join non-multicast address", ip4_addr_ismulticast(groupaddr), return ERR_VAL;);
+ LWIP_ERROR("igmp_joingroup_netif: attempt to join allsystems address", (!ip4_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;);
+
+ /* make sure it is an igmp-enabled netif */
+ LWIP_ERROR("igmp_joingroup_netif: attempt to join on non-IGMP netif", netif->flags & NETIF_FLAG_IGMP, return ERR_VAL;);
+
+ /* find group or create a new one if not found */
+ group = igmp_lookup_group(netif, groupaddr);
+
+ if (group != NULL) {
+ /* This should create a new group, check the state to make sure */
+ if (group->group_state != IGMP_GROUP_NON_MEMBER) {
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup_netif: join to group not in state IGMP_GROUP_NON_MEMBER\n"));
+ } else {
+ /* OK - it was new group */
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup_netif: join to new group: "));
+ ip4_addr_debug_print(IGMP_DEBUG, groupaddr);
+ LWIP_DEBUGF(IGMP_DEBUG, ("\n"));
+
+ /* If first use of the group, allow the group at the MAC level */
+ if ((group->use==0) && (netif->igmp_mac_filter != NULL)) {
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup_netif: igmp_mac_filter(ADD "));
+ ip4_addr_debug_print(IGMP_DEBUG, groupaddr);
+ LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", (void*)netif));
+ netif->igmp_mac_filter(netif, groupaddr, NETIF_ADD_MAC_FILTER);
+ }
+
+ IGMP_STATS_INC(igmp.tx_join);
+ igmp_send(netif, group, IGMP_V2_MEMB_REPORT);
+
+ igmp_start_timer(group, IGMP_JOIN_DELAYING_MEMBER_TMR);
+
+ /* Need to work out where this timer comes from */
+ group->group_state = IGMP_GROUP_DELAYING_MEMBER;
+ }
+ /* Increment group use */
+ group->use++;
+ /* Join on this interface */
+ return ERR_OK;
+ } else {
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup_netif: Not enough memory to join to group\n"));
+ return ERR_MEM;
+ }
+}
+
+/**
+ * @ingroup igmp
+ * Leave a group on one network interface.
+ *
+ * @param ifaddr ip address of the network interface which should leave a group
+ * @param groupaddr the ip address of the group which to leave
+ * @return ERR_OK if group was left on the netif(s), an err_t otherwise
+ */
+err_t
+igmp_leavegroup(const ip4_addr_t *ifaddr, const ip4_addr_t *groupaddr)
+{
+ err_t err = ERR_VAL; /* no matching interface */
+ struct netif *netif;
+
+ /* make sure it is multicast address */
+ LWIP_ERROR("igmp_leavegroup: attempt to leave non-multicast address", ip4_addr_ismulticast(groupaddr), return ERR_VAL;);
+ LWIP_ERROR("igmp_leavegroup: attempt to leave allsystems address", (!ip4_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;);
+
+ /* loop through netif's */
+ netif = netif_list;
+ while (netif != NULL) {
+ /* Should we leave this interface ? */
+ if ((netif->flags & NETIF_FLAG_IGMP) && ((ip4_addr_isany(ifaddr) || ip4_addr_cmp(netif_ip4_addr(netif), ifaddr)))) {
+ err_t res = igmp_leavegroup_netif(netif, groupaddr);
+ if (err != ERR_OK) {
+ /* Store this result if we have not yet gotten a success */
+ err = res;
+ }
+ }
+ /* proceed to next network interface */
+ netif = netif->next;
+ }
+
+ return err;
+}
+
+/**
+ * @ingroup igmp
+ * Leave a group on one network interface.
+ *
+ * @param netif the network interface which should leave a group
+ * @param groupaddr the ip address of the group which to leave
+ * @return ERR_OK if group was left on the netif, an err_t otherwise
+ */
+err_t
+igmp_leavegroup_netif(struct netif *netif, const ip4_addr_t *groupaddr)
+{
+ struct igmp_group *group;
+
+ /* make sure it is multicast address */
+ LWIP_ERROR("igmp_leavegroup_netif: attempt to leave non-multicast address", ip4_addr_ismulticast(groupaddr), return ERR_VAL;);
+ LWIP_ERROR("igmp_leavegroup_netif: attempt to leave allsystems address", (!ip4_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;);
+
+ /* make sure it is an igmp-enabled netif */
+ LWIP_ERROR("igmp_leavegroup_netif: attempt to leave on non-IGMP netif", netif->flags & NETIF_FLAG_IGMP, return ERR_VAL;);
+
+ /* find group */
+ group = igmp_lookfor_group(netif, groupaddr);
+
+ if (group != NULL) {
+ /* Only send a leave if the flag is set according to the state diagram */
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup_netif: Leaving group: "));
+ ip4_addr_debug_print(IGMP_DEBUG, groupaddr);
+ LWIP_DEBUGF(IGMP_DEBUG, ("\n"));
+
+ /* If there is no other use of the group */
+ if (group->use <= 1) {
+ /* Remove the group from the list */
+ igmp_remove_group(netif, group);
+
+ /* If we are the last reporter for this group */
+ if (group->last_reporter_flag) {
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup_netif: sending leaving group\n"));
+ IGMP_STATS_INC(igmp.tx_leave);
+ igmp_send(netif, group, IGMP_LEAVE_GROUP);
+ }
+
+ /* Disable the group at the MAC level */
+ if (netif->igmp_mac_filter != NULL) {
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup_netif: igmp_mac_filter(DEL "));
+ ip4_addr_debug_print(IGMP_DEBUG, groupaddr);
+ LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", (void*)netif));
+ netif->igmp_mac_filter(netif, groupaddr, NETIF_DEL_MAC_FILTER);
+ }
+
+ /* Free group struct */
+ memp_free(MEMP_IGMP_GROUP, group);
+ } else {
+ /* Decrement group use */
+ group->use--;
+ }
+ return ERR_OK;
+ } else {
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup_netif: not member of group\n"));
+ return ERR_VAL;
+ }
+}
+
+/**
+ * The igmp timer function (both for NO_SYS=1 and =0)
+ * Should be called every IGMP_TMR_INTERVAL milliseconds (100 ms is default).
+ */
+void
+igmp_tmr(void)
+{
+ struct netif *netif = netif_list;
+
+ while (netif != NULL) {
+ struct igmp_group *group = netif_igmp_data(netif);
+
+ while (group != NULL) {
+ if (group->timer > 0) {
+ group->timer--;
+ if (group->timer == 0) {
+ igmp_timeout(netif, group);
+ }
+ }
+ group = group->next;
+ }
+ netif = netif->next;
+ }
+}
+
+/**
+ * Called if a timeout for one group is reached.
+ * Sends a report for this group.
+ *
+ * @param group an igmp_group for which a timeout is reached
+ */
+static void
+igmp_timeout(struct netif *netif, struct igmp_group *group)
+{
+ /* If the state is IGMP_GROUP_DELAYING_MEMBER then we send a report for this group
+ (unless it is the allsystems group) */
+ if ((group->group_state == IGMP_GROUP_DELAYING_MEMBER) &&
+ (!(ip4_addr_cmp(&(group->group_address), &allsystems)))) {
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_timeout: report membership for group with address "));
+ ip4_addr_debug_print(IGMP_DEBUG, &(group->group_address));
+ LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", (void*)netif));
+
+ group->group_state = IGMP_GROUP_IDLE_MEMBER;
+
+ IGMP_STATS_INC(igmp.tx_report);
+ igmp_send(netif, group, IGMP_V2_MEMB_REPORT);
+ }
+}
+
+/**
+ * Start a timer for an igmp group
+ *
+ * @param group the igmp_group for which to start a timer
+ * @param max_time the time in multiples of IGMP_TMR_INTERVAL (decrease with
+ * every call to igmp_tmr())
+ */
+static void
+igmp_start_timer(struct igmp_group *group, u8_t max_time)
+{
+#ifdef LWIP_RAND
+ group->timer = max_time > 2 ? (LWIP_RAND() % max_time) : 1;
+#else /* LWIP_RAND */
+ /* ATTENTION: use this only if absolutely necessary! */
+ group->timer = max_time / 2;
+#endif /* LWIP_RAND */
+
+ if (group->timer == 0) {
+ group->timer = 1;
+ }
+}
+
+/**
+ * Delaying membership report for a group if necessary
+ *
+ * @param group the igmp_group for which "delaying" membership report
+ * @param maxresp query delay
+ */
+static void
+igmp_delaying_member(struct igmp_group *group, u8_t maxresp)
+{
+ if ((group->group_state == IGMP_GROUP_IDLE_MEMBER) ||
+ ((group->group_state == IGMP_GROUP_DELAYING_MEMBER) &&
+ ((group->timer == 0) || (maxresp < group->timer)))) {
+ igmp_start_timer(group, maxresp);
+ group->group_state = IGMP_GROUP_DELAYING_MEMBER;
+ }
+}
+
+
+/**
+ * Sends an IP packet on a network interface. This function constructs the IP header
+ * and calculates the IP header checksum. If the source IP address is NULL,
+ * the IP address of the outgoing network interface is filled in as source address.
+ *
+ * @param p the packet to send (p->payload points to the data, e.g. next
+ protocol header; if dest == LWIP_IP_HDRINCL, p already includes an
+ IP header and p->payload points to that IP header)
+ * @param src the source IP address to send from (if src == IP4_ADDR_ANY, the
+ * IP address of the netif used to send is used as source address)
+ * @param dest the destination IP address to send the packet to
+ * @param netif the netif on which to send this packet
+ * @return ERR_OK if the packet was sent OK
+ * ERR_BUF if p doesn't have enough space for IP/LINK headers
+ * returns errors returned by netif->output
+ */
+static err_t
+igmp_ip_output_if(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest, struct netif *netif)
+{
+ /* This is the "router alert" option */
+ u16_t ra[2];
+ ra[0] = PP_HTONS(ROUTER_ALERT);
+ ra[1] = 0x0000; /* Router shall examine packet */
+ IGMP_STATS_INC(igmp.xmit);
+ return ip4_output_if_opt(p, src, dest, IGMP_TTL, 0, IP_PROTO_IGMP, netif, ra, ROUTER_ALERTLEN);
+}
+
+/**
+ * Send an igmp packet to a specific group.
+ *
+ * @param group the group to which to send the packet
+ * @param type the type of igmp packet to send
+ */
+static void
+igmp_send(struct netif *netif, struct igmp_group *group, u8_t type)
+{
+ struct pbuf* p = NULL;
+ struct igmp_msg* igmp = NULL;
+ ip4_addr_t src = *IP4_ADDR_ANY4;
+ ip4_addr_t* dest = NULL;
+
+ /* IP header + "router alert" option + IGMP header */
+ p = pbuf_alloc(PBUF_TRANSPORT, IGMP_MINLEN, PBUF_RAM);
+
+ if (p) {
+ igmp = (struct igmp_msg *)p->payload;
+ LWIP_ASSERT("igmp_send: check that first pbuf can hold struct igmp_msg",
+ (p->len >= sizeof(struct igmp_msg)));
+ ip4_addr_copy(src, *netif_ip4_addr(netif));
+
+ if (type == IGMP_V2_MEMB_REPORT) {
+ dest = &(group->group_address);
+ ip4_addr_copy(igmp->igmp_group_address, group->group_address);
+ group->last_reporter_flag = 1; /* Remember we were the last to report */
+ } else {
+ if (type == IGMP_LEAVE_GROUP) {
+ dest = &allrouters;
+ ip4_addr_copy(igmp->igmp_group_address, group->group_address);
+ }
+ }
+
+ if ((type == IGMP_V2_MEMB_REPORT) || (type == IGMP_LEAVE_GROUP)) {
+ igmp->igmp_msgtype = type;
+ igmp->igmp_maxresp = 0;
+ igmp->igmp_checksum = 0;
+ igmp->igmp_checksum = inet_chksum(igmp, IGMP_MINLEN);
+
+ igmp_ip_output_if(p, &src, dest, netif);
+ }
+
+ pbuf_free(p);
+ } else {
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_send: not enough memory for igmp_send\n"));
+ IGMP_STATS_INC(igmp.memerr);
+ }
+}
+
+#endif /* LWIP_IPV4 && LWIP_IGMP */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/ipv4/ip4.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/ipv4/ip4.c
new file mode 100644
index 0000000..4e4eb61
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/ipv4/ip4.c
@@ -0,0 +1,1086 @@
+/**
+ * @file
+ * This is the IPv4 layer implementation for incoming and outgoing IP traffic.
+ *
+ * @see ip_frag.c
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV4
+
+#include "lwip/ip.h"
+#include "lwip/def.h"
+#include "lwip/mem.h"
+#include "lwip/ip4_frag.h"
+#include "lwip/inet_chksum.h"
+#include "lwip/netif.h"
+#include "lwip/icmp.h"
+#include "lwip/igmp.h"
+#include "lwip/raw.h"
+#include "lwip/udp.h"
+#include "lwip/priv/tcp_priv.h"
+#include "lwip/autoip.h"
+#include "lwip/stats.h"
+#include "lwip/prot/dhcp.h"
+
+#include <string.h>
+
+#ifdef LWIP_HOOK_FILENAME
+#include LWIP_HOOK_FILENAME
+#endif
+
+/** Set this to 0 in the rare case of wanting to call an extra function to
+ * generate the IP checksum (in contrast to calculating it on-the-fly). */
+#ifndef LWIP_INLINE_IP_CHKSUM
+#if LWIP_CHECKSUM_CTRL_PER_NETIF
+#define LWIP_INLINE_IP_CHKSUM 0
+#else /* LWIP_CHECKSUM_CTRL_PER_NETIF */
+#define LWIP_INLINE_IP_CHKSUM 1
+#endif /* LWIP_CHECKSUM_CTRL_PER_NETIF */
+#endif
+
+#if LWIP_INLINE_IP_CHKSUM && CHECKSUM_GEN_IP
+#define CHECKSUM_GEN_IP_INLINE 1
+#else
+#define CHECKSUM_GEN_IP_INLINE 0
+#endif
+
+#if LWIP_DHCP || defined(LWIP_IP_ACCEPT_UDP_PORT)
+#define IP_ACCEPT_LINK_LAYER_ADDRESSING 1
+
+/** Some defines for DHCP to let link-layer-addressed packets through while the
+ * netif is down.
+ * To use this in your own application/protocol, define LWIP_IP_ACCEPT_UDP_PORT(port)
+ * to return 1 if the port is accepted and 0 if the port is not accepted.
+ */
+#if LWIP_DHCP && defined(LWIP_IP_ACCEPT_UDP_PORT)
+/* accept DHCP client port and custom port */
+#define IP_ACCEPT_LINK_LAYER_ADDRESSED_PORT(port) (((port) == PP_NTOHS(DHCP_CLIENT_PORT)) \
+ || (LWIP_IP_ACCEPT_UDP_PORT(port)))
+#elif defined(LWIP_IP_ACCEPT_UDP_PORT) /* LWIP_DHCP && defined(LWIP_IP_ACCEPT_UDP_PORT) */
+/* accept custom port only */
+#define IP_ACCEPT_LINK_LAYER_ADDRESSED_PORT(port) (LWIP_IP_ACCEPT_UDP_PORT(port))
+#else /* LWIP_DHCP && defined(LWIP_IP_ACCEPT_UDP_PORT) */
+/* accept DHCP client port only */
+#define IP_ACCEPT_LINK_LAYER_ADDRESSED_PORT(port) ((port) == PP_NTOHS(DHCP_CLIENT_PORT))
+#endif /* LWIP_DHCP && defined(LWIP_IP_ACCEPT_UDP_PORT) */
+
+#else /* LWIP_DHCP */
+#define IP_ACCEPT_LINK_LAYER_ADDRESSING 0
+#endif /* LWIP_DHCP */
+
+/** The IP header ID of the next outgoing IP packet */
+static u16_t ip_id;
+
+#if LWIP_MULTICAST_TX_OPTIONS
+/** The default netif used for multicast */
+static struct netif* ip4_default_multicast_netif;
+
+/**
+ * @ingroup ip4
+ * Set a default netif for IPv4 multicast. */
+void
+ip4_set_default_multicast_netif(struct netif* default_multicast_netif)
+{
+ ip4_default_multicast_netif = default_multicast_netif;
+}
+#endif /* LWIP_MULTICAST_TX_OPTIONS */
+
+#ifdef LWIP_HOOK_IP4_ROUTE_SRC
+/**
+ * Source based IPv4 routing must be fully implemented in
+ * LWIP_HOOK_IP4_ROUTE_SRC(). This function only provides he parameters.
+ */
+struct netif *
+ip4_route_src(const ip4_addr_t *dest, const ip4_addr_t *src)
+{
+ if (src != NULL) {
+ /* when src==NULL, the hook is called from ip4_route(dest) */
+ struct netif *netif = LWIP_HOOK_IP4_ROUTE_SRC(dest, src);
+ if (netif != NULL) {
+ return netif;
+ }
+ }
+ return ip4_route(dest);
+}
+#endif /* LWIP_HOOK_IP4_ROUTE_SRC */
+
+/**
+ * Finds the appropriate network interface for a given IP address. It
+ * searches the list of network interfaces linearly. A match is found
+ * if the masked IP address of the network interface equals the masked
+ * IP address given to the function.
+ *
+ * @param dest the destination IP address for which to find the route
+ * @return the netif on which to send to reach dest
+ */
+struct netif *
+ip4_route(const ip4_addr_t *dest)
+{
+ struct netif *netif;
+
+#if LWIP_MULTICAST_TX_OPTIONS
+ /* Use administratively selected interface for multicast by default */
+ if (ip4_addr_ismulticast(dest) && ip4_default_multicast_netif) {
+ return ip4_default_multicast_netif;
+ }
+#endif /* LWIP_MULTICAST_TX_OPTIONS */
+
+ /* iterate through netifs */
+ for (netif = netif_list; netif != NULL; netif = netif->next) {
+ /* is the netif up, does it have a link and a valid address? */
+ if (netif_is_up(netif) && netif_is_link_up(netif) && !ip4_addr_isany_val(*netif_ip4_addr(netif))) {
+ /* network mask matches? */
+ if (ip4_addr_netcmp(dest, netif_ip4_addr(netif), netif_ip4_netmask(netif))) {
+ /* return netif on which to forward IP packet */
+ return netif;
+ }
+ /* gateway matches on a non broadcast interface? (i.e. peer in a point to point interface) */
+ if (((netif->flags & NETIF_FLAG_BROADCAST) == 0) && ip4_addr_cmp(dest, netif_ip4_gw(netif))) {
+ /* return netif on which to forward IP packet */
+ return netif;
+ }
+ }
+ }
+
+#if LWIP_NETIF_LOOPBACK && !LWIP_HAVE_LOOPIF
+ /* loopif is disabled, looopback traffic is passed through any netif */
+ if (ip4_addr_isloopback(dest)) {
+ /* don't check for link on loopback traffic */
+ if (netif_default != NULL && netif_is_up(netif_default)) {
+ return netif_default;
+ }
+ /* default netif is not up, just use any netif for loopback traffic */
+ for (netif = netif_list; netif != NULL; netif = netif->next) {
+ if (netif_is_up(netif)) {
+ return netif;
+ }
+ }
+ return NULL;
+ }
+#endif /* LWIP_NETIF_LOOPBACK && !LWIP_HAVE_LOOPIF */
+
+#ifdef LWIP_HOOK_IP4_ROUTE_SRC
+ netif = LWIP_HOOK_IP4_ROUTE_SRC(dest, NULL);
+ if (netif != NULL) {
+ return netif;
+ }
+#elif defined(LWIP_HOOK_IP4_ROUTE)
+ netif = LWIP_HOOK_IP4_ROUTE(dest);
+ if (netif != NULL) {
+ return netif;
+ }
+#endif
+
+ if ((netif_default == NULL) || !netif_is_up(netif_default) || !netif_is_link_up(netif_default) ||
+ ip4_addr_isany_val(*netif_ip4_addr(netif_default))) {
+ /* No matching netif found and default netif is not usable.
+ If this is not good enough for you, use LWIP_HOOK_IP4_ROUTE() */
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip4_route: No route to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ ip4_addr1_16(dest), ip4_addr2_16(dest), ip4_addr3_16(dest), ip4_addr4_16(dest)));
+ IP_STATS_INC(ip.rterr);
+ MIB2_STATS_INC(mib2.ipoutnoroutes);
+ return NULL;
+ }
+
+ return netif_default;
+}
+
+#if IP_FORWARD
+/**
+ * Determine whether an IP address is in a reserved set of addresses
+ * that may not be forwarded, or whether datagrams to that destination
+ * may be forwarded.
+ * @param p the packet to forward
+ * @return 1: can forward 0: discard
+ */
+static int
+ip4_canforward(struct pbuf *p)
+{
+ u32_t addr = lwip_htonl(ip4_addr_get_u32(ip4_current_dest_addr()));
+
+ if (p->flags & PBUF_FLAG_LLBCAST) {
+ /* don't route link-layer broadcasts */
+ return 0;
+ }
+ if ((p->flags & PBUF_FLAG_LLMCAST) && !IP_MULTICAST(addr)) {
+ /* don't route link-layer multicasts unless the destination address is an IP
+ multicast address */
+ return 0;
+ }
+ if (IP_EXPERIMENTAL(addr)) {
+ return 0;
+ }
+ if (IP_CLASSA(addr)) {
+ u32_t net = addr & IP_CLASSA_NET;
+ if ((net == 0) || (net == ((u32_t)IP_LOOPBACKNET << IP_CLASSA_NSHIFT))) {
+ /* don't route loopback packets */
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/**
+ * Forwards an IP packet. It finds an appropriate route for the
+ * packet, decrements the TTL value of the packet, adjusts the
+ * checksum and outputs the packet on the appropriate interface.
+ *
+ * @param p the packet to forward (p->payload points to IP header)
+ * @param iphdr the IP header of the input packet
+ * @param inp the netif on which this packet was received
+ */
+static void
+ip4_forward(struct pbuf *p, struct ip_hdr *iphdr, struct netif *inp)
+{
+ struct netif *netif;
+
+ PERF_START;
+ LWIP_UNUSED_ARG(inp);
+
+ if (!ip4_canforward(p)) {
+ goto return_noroute;
+ }
+
+ /* RFC3927 2.7: do not forward link-local addresses */
+ if (ip4_addr_islinklocal(ip4_current_dest_addr())) {
+ LWIP_DEBUGF(IP_DEBUG, ("ip4_forward: not forwarding LLA %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ ip4_addr1_16(ip4_current_dest_addr()), ip4_addr2_16(ip4_current_dest_addr()),
+ ip4_addr3_16(ip4_current_dest_addr()), ip4_addr4_16(ip4_current_dest_addr())));
+ goto return_noroute;
+ }
+
+ /* Find network interface where to forward this IP packet to. */
+ netif = ip4_route_src(ip4_current_dest_addr(), ip4_current_src_addr());
+ if (netif == NULL) {
+ LWIP_DEBUGF(IP_DEBUG, ("ip4_forward: no forwarding route for %"U16_F".%"U16_F".%"U16_F".%"U16_F" found\n",
+ ip4_addr1_16(ip4_current_dest_addr()), ip4_addr2_16(ip4_current_dest_addr()),
+ ip4_addr3_16(ip4_current_dest_addr()), ip4_addr4_16(ip4_current_dest_addr())));
+ /* @todo: send ICMP_DUR_NET? */
+ goto return_noroute;
+ }
+#if !IP_FORWARD_ALLOW_TX_ON_RX_NETIF
+ /* Do not forward packets onto the same network interface on which
+ * they arrived. */
+ if (netif == inp) {
+ LWIP_DEBUGF(IP_DEBUG, ("ip4_forward: not bouncing packets back on incoming interface.\n"));
+ goto return_noroute;
+ }
+#endif /* IP_FORWARD_ALLOW_TX_ON_RX_NETIF */
+
+ /* decrement TTL */
+ IPH_TTL_SET(iphdr, IPH_TTL(iphdr) - 1);
+ /* send ICMP if TTL == 0 */
+ if (IPH_TTL(iphdr) == 0) {
+ MIB2_STATS_INC(mib2.ipinhdrerrors);
+#if LWIP_ICMP
+ /* Don't send ICMP messages in response to ICMP messages */
+ if (IPH_PROTO(iphdr) != IP_PROTO_ICMP) {
+ icmp_time_exceeded(p, ICMP_TE_TTL);
+ }
+#endif /* LWIP_ICMP */
+ return;
+ }
+
+ /* Incrementally update the IP checksum. */
+ if (IPH_CHKSUM(iphdr) >= PP_HTONS(0xffffU - 0x100)) {
+ IPH_CHKSUM_SET(iphdr, IPH_CHKSUM(iphdr) + PP_HTONS(0x100) + 1);
+ } else {
+ IPH_CHKSUM_SET(iphdr, IPH_CHKSUM(iphdr) + PP_HTONS(0x100));
+ }
+
+ LWIP_DEBUGF(IP_DEBUG, ("ip4_forward: forwarding packet to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ ip4_addr1_16(ip4_current_dest_addr()), ip4_addr2_16(ip4_current_dest_addr()),
+ ip4_addr3_16(ip4_current_dest_addr()), ip4_addr4_16(ip4_current_dest_addr())));
+
+ IP_STATS_INC(ip.fw);
+ MIB2_STATS_INC(mib2.ipforwdatagrams);
+ IP_STATS_INC(ip.xmit);
+
+ PERF_STOP("ip4_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
+ ip4_frag(p, netif, ip4_current_dest_addr());
+#else /* IP_FRAG */
+ /* @todo: send ICMP Destination Unreachable code 13 "Communication administratively prohibited"? */
+#endif /* IP_FRAG */
+ } else {
+#if LWIP_ICMP
+ /* send ICMP Destination Unreachable code 4: "Fragmentation Needed and DF Set" */
+ icmp_dest_unreach(p, ICMP_DUR_FRAG);
+#endif /* LWIP_ICMP */
+ }
+ return;
+ }
+ /* transmit pbuf on chosen interface */
+ netif->output(netif, p, ip4_current_dest_addr());
+ return;
+return_noroute:
+ MIB2_STATS_INC(mib2.ipoutnoroutes);
+}
+#endif /* IP_FORWARD */
+
+/**
+ * This function is called by the network interface device driver when
+ * an IP packet is received. The function does the basic checks of the
+ * IP header such as packet size being at least larger than the header
+ * size etc. If the packet was not destined for us, the packet is
+ * forwarded (using ip_forward). The IP checksum is always checked.
+ *
+ * Finally, the packet is sent to the upper layer protocol input function.
+ *
+ * @param p the received IP packet (p->payload points to IP header)
+ * @param inp the netif on which this packet was received
+ * @return ERR_OK if the packet was processed (could return ERR_* if it wasn't
+ * processed, but currently always returns ERR_OK)
+ */
+err_t
+ip4_input(struct pbuf *p, struct netif *inp)
+{
+ struct ip_hdr *iphdr;
+ struct netif *netif;
+ u16_t iphdr_hlen;
+ u16_t iphdr_len;
+#if IP_ACCEPT_LINK_LAYER_ADDRESSING || LWIP_IGMP
+ int check_ip_src = 1;
+#endif /* IP_ACCEPT_LINK_LAYER_ADDRESSING || LWIP_IGMP */
+
+ IP_STATS_INC(ip.recv);
+ MIB2_STATS_INC(mib2.ipinreceives);
+
+ /* identify the IP header */
+ iphdr = (struct ip_hdr *)p->payload;
+ if (IPH_V(iphdr) != 4) {
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_WARNING, ("IP packet dropped due to bad version number %"U16_F"\n", (u16_t)IPH_V(iphdr)));
+ ip4_debug_print(p);
+ pbuf_free(p);
+ IP_STATS_INC(ip.err);
+ IP_STATS_INC(ip.drop);
+ MIB2_STATS_INC(mib2.ipinhdrerrors);
+ return ERR_OK;
+ }
+
+#ifdef LWIP_HOOK_IP4_INPUT
+ if (LWIP_HOOK_IP4_INPUT(p, inp)) {
+ /* the packet has been eaten */
+ return ERR_OK;
+ }
+#endif
+
+ /* obtain IP header length in number of 32-bit words */
+ iphdr_hlen = IPH_HL(iphdr);
+ /* calculate IP header length in bytes */
+ iphdr_hlen *= 4;
+ /* obtain ip length in bytes */
+ iphdr_len = lwip_ntohs(IPH_LEN(iphdr));
+
+ /* Trim pbuf. This is especially required for packets < 60 bytes. */
+ if (iphdr_len < p->tot_len) {
+ pbuf_realloc(p, iphdr_len);
+ }
+
+ /* header length exceeds first pbuf length, or ip length exceeds total pbuf length? */
+ if ((iphdr_hlen > p->len) || (iphdr_len > p->tot_len) || (iphdr_hlen < IP_HLEN)) {
+ if (iphdr_hlen < IP_HLEN) {
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+ ("ip4_input: short IP header (%"U16_F" bytes) received, IP packet dropped\n", iphdr_hlen));
+ }
+ if (iphdr_hlen > p->len) {
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+ ("IP header (len %"U16_F") does not fit in first pbuf (len %"U16_F"), IP packet dropped.\n",
+ iphdr_hlen, p->len));
+ }
+ if (iphdr_len > p->tot_len) {
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+ ("IP (len %"U16_F") is longer than pbuf (len %"U16_F"), IP packet dropped.\n",
+ iphdr_len, p->tot_len));
+ }
+ /* free (drop) packet pbufs */
+ pbuf_free(p);
+ IP_STATS_INC(ip.lenerr);
+ IP_STATS_INC(ip.drop);
+ MIB2_STATS_INC(mib2.ipindiscards);
+ return ERR_OK;
+ }
+
+ /* verify checksum */
+#if CHECKSUM_CHECK_IP
+ IF__NETIF_CHECKSUM_ENABLED(inp, NETIF_CHECKSUM_CHECK_IP) {
+ if (inet_chksum(iphdr, iphdr_hlen) != 0) {
+
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+ ("Checksum (0x%"X16_F") failed, IP packet dropped.\n", inet_chksum(iphdr, iphdr_hlen)));
+ ip4_debug_print(p);
+ pbuf_free(p);
+ IP_STATS_INC(ip.chkerr);
+ IP_STATS_INC(ip.drop);
+ MIB2_STATS_INC(mib2.ipinhdrerrors);
+ return ERR_OK;
+ }
+ }
+#endif
+
+ /* copy IP addresses to aligned ip_addr_t */
+ ip_addr_copy_from_ip4(ip_data.current_iphdr_dest, iphdr->dest);
+ ip_addr_copy_from_ip4(ip_data.current_iphdr_src, iphdr->src);
+
+ /* match packet against an interface, i.e. is this packet for us? */
+ if (ip4_addr_ismulticast(ip4_current_dest_addr())) {
+#if LWIP_IGMP
+ if ((inp->flags & NETIF_FLAG_IGMP) && (igmp_lookfor_group(inp, ip4_current_dest_addr()))) {
+ /* IGMP snooping switches need 0.0.0.0 to be allowed as source address (RFC 4541) */
+ ip4_addr_t allsystems;
+ IP4_ADDR(&allsystems, 224, 0, 0, 1);
+ if (ip4_addr_cmp(ip4_current_dest_addr(), &allsystems) &&
+ ip4_addr_isany(ip4_current_src_addr())) {
+ check_ip_src = 0;
+ }
+ netif = inp;
+ } else {
+ netif = NULL;
+ }
+#else /* LWIP_IGMP */
+ if ((netif_is_up(inp)) && (!ip4_addr_isany_val(*netif_ip4_addr(inp)))) {
+ netif = inp;
+ } else {
+ netif = NULL;
+ }
+#endif /* LWIP_IGMP */
+ } else {
+ /* start trying with inp. if that's not acceptable, start walking the
+ list of configured netifs.
+ 'first' is used as a boolean to mark whether we started walking the list */
+ int first = 1;
+ netif = inp;
+ do {
+ LWIP_DEBUGF(IP_DEBUG, ("ip_input: iphdr->dest 0x%"X32_F" netif->ip_addr 0x%"X32_F" (0x%"X32_F", 0x%"X32_F", 0x%"X32_F")\n",
+ ip4_addr_get_u32(&iphdr->dest), ip4_addr_get_u32(netif_ip4_addr(netif)),
+ ip4_addr_get_u32(&iphdr->dest) & ip4_addr_get_u32(netif_ip4_netmask(netif)),
+ ip4_addr_get_u32(netif_ip4_addr(netif)) & ip4_addr_get_u32(netif_ip4_netmask(netif)),
+ ip4_addr_get_u32(&iphdr->dest) & ~ip4_addr_get_u32(netif_ip4_netmask(netif))));
+
+ /* interface is up and configured? */
+ if ((netif_is_up(netif)) && (!ip4_addr_isany_val(*netif_ip4_addr(netif)))) {
+ /* unicast to this interface address? */
+ if (ip4_addr_cmp(ip4_current_dest_addr(), netif_ip4_addr(netif)) ||
+ /* or broadcast on this interface network address? */
+ ip4_addr_isbroadcast(ip4_current_dest_addr(), netif)
+#if LWIP_NETIF_LOOPBACK && !LWIP_HAVE_LOOPIF
+ || (ip4_addr_get_u32(ip4_current_dest_addr()) == PP_HTONL(IPADDR_LOOPBACK))
+#endif /* LWIP_NETIF_LOOPBACK && !LWIP_HAVE_LOOPIF */
+ ) {
+ LWIP_DEBUGF(IP_DEBUG, ("ip4_input: packet accepted on interface %c%c\n",
+ netif->name[0], netif->name[1]));
+ /* break out of for loop */
+ break;
+ }
+#if LWIP_AUTOIP
+ /* connections to link-local addresses must persist after changing
+ the netif's address (RFC3927 ch. 1.9) */
+ if (autoip_accept_packet(netif, ip4_current_dest_addr())) {
+ LWIP_DEBUGF(IP_DEBUG, ("ip4_input: LLA packet accepted on interface %c%c\n",
+ netif->name[0], netif->name[1]));
+ /* break out of for loop */
+ break;
+ }
+#endif /* LWIP_AUTOIP */
+ }
+ if (first) {
+#if !LWIP_NETIF_LOOPBACK || LWIP_HAVE_LOOPIF
+ /* Packets sent to the loopback address must not be accepted on an
+ * interface that does not have the loopback address assigned to it,
+ * unless a non-loopback interface is used for loopback traffic. */
+ if (ip4_addr_isloopback(ip4_current_dest_addr())) {
+ netif = NULL;
+ break;
+ }
+#endif /* !LWIP_NETIF_LOOPBACK || LWIP_HAVE_LOOPIF */
+ first = 0;
+ netif = netif_list;
+ } else {
+ netif = netif->next;
+ }
+ if (netif == inp) {
+ netif = netif->next;
+ }
+ } while (netif != NULL);
+ }
+
+#if IP_ACCEPT_LINK_LAYER_ADDRESSING
+ /* Pass DHCP messages regardless of destination address. DHCP traffic is addressed
+ * using link layer addressing (such as Ethernet MAC) so we must not filter on IP.
+ * According to RFC 1542 section 3.1.1, referred by RFC 2131).
+ *
+ * If you want to accept private broadcast communication while a netif is down,
+ * define LWIP_IP_ACCEPT_UDP_PORT(dst_port), e.g.:
+ *
+ * #define LWIP_IP_ACCEPT_UDP_PORT(dst_port) ((dst_port) == PP_NTOHS(12345))
+ */
+ if (netif == NULL) {
+ /* remote port is DHCP server? */
+ if (IPH_PROTO(iphdr) == IP_PROTO_UDP) {
+ struct udp_hdr *udphdr = (struct udp_hdr *)((u8_t *)iphdr + iphdr_hlen);
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE, ("ip4_input: UDP packet to DHCP client port %"U16_F"\n",
+ lwip_ntohs(udphdr->dest)));
+ if (IP_ACCEPT_LINK_LAYER_ADDRESSED_PORT(udphdr->dest)) {
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE, ("ip4_input: DHCP packet accepted.\n"));
+ netif = inp;
+ check_ip_src = 0;
+ }
+ }
+ }
+#endif /* IP_ACCEPT_LINK_LAYER_ADDRESSING */
+
+ /* broadcast or multicast packet source address? Compliant with RFC 1122: 3.2.1.3 */
+#if LWIP_IGMP || IP_ACCEPT_LINK_LAYER_ADDRESSING
+ if (check_ip_src
+#if IP_ACCEPT_LINK_LAYER_ADDRESSING
+ /* DHCP servers need 0.0.0.0 to be allowed as source address (RFC 1.1.2.2: 3.2.1.3/a) */
+ && !ip4_addr_isany_val(*ip4_current_src_addr())
+#endif /* IP_ACCEPT_LINK_LAYER_ADDRESSING */
+ )
+#endif /* LWIP_IGMP || IP_ACCEPT_LINK_LAYER_ADDRESSING */
+ {
+ if ((ip4_addr_isbroadcast(ip4_current_src_addr(), inp)) ||
+ (ip4_addr_ismulticast(ip4_current_src_addr()))) {
+ /* packet source is not valid */
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("ip4_input: packet source is not valid.\n"));
+ /* free (drop) packet pbufs */
+ pbuf_free(p);
+ IP_STATS_INC(ip.drop);
+ MIB2_STATS_INC(mib2.ipinaddrerrors);
+ MIB2_STATS_INC(mib2.ipindiscards);
+ return ERR_OK;
+ }
+ }
+
+ /* packet not for us? */
+ if (netif == NULL) {
+ /* packet not for us, route or discard */
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE, ("ip4_input: packet not for us.\n"));
+#if IP_FORWARD
+ /* non-broadcast packet? */
+ if (!ip4_addr_isbroadcast(ip4_current_dest_addr(), inp)) {
+ /* try to forward IP packet on (other) interfaces */
+ ip4_forward(p, iphdr, inp);
+ } else
+#endif /* IP_FORWARD */
+ {
+ IP_STATS_INC(ip.drop);
+ MIB2_STATS_INC(mib2.ipinaddrerrors);
+ MIB2_STATS_INC(mib2.ipindiscards);
+ }
+ pbuf_free(p);
+ return ERR_OK;
+ }
+ /* packet consists of multiple fragments? */
+ if ((IPH_OFFSET(iphdr) & PP_HTONS(IP_OFFMASK | IP_MF)) != 0) {
+#if IP_REASSEMBLY /* packet fragment reassembly code present? */
+ LWIP_DEBUGF(IP_DEBUG, ("IP packet is a fragment (id=0x%04"X16_F" tot_len=%"U16_F" len=%"U16_F" MF=%"U16_F" offset=%"U16_F"), calling ip4_reass()\n",
+ lwip_ntohs(IPH_ID(iphdr)), p->tot_len, lwip_ntohs(IPH_LEN(iphdr)), (u16_t)!!(IPH_OFFSET(iphdr) & PP_HTONS(IP_MF)), (u16_t)((lwip_ntohs(IPH_OFFSET(iphdr)) & IP_OFFMASK)*8)));
+ /* reassemble the packet*/
+ p = ip4_reass(p);
+ /* packet not fully reassembled yet? */
+ if (p == NULL) {
+ return ERR_OK;
+ }
+ iphdr = (struct ip_hdr *)p->payload;
+#else /* IP_REASSEMBLY == 0, no packet fragment reassembly code present */
+ pbuf_free(p);
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("IP packet dropped since it was fragmented (0x%"X16_F") (while IP_REASSEMBLY == 0).\n",
+ lwip_ntohs(IPH_OFFSET(iphdr))));
+ IP_STATS_INC(ip.opterr);
+ IP_STATS_INC(ip.drop);
+ /* unsupported protocol feature */
+ MIB2_STATS_INC(mib2.ipinunknownprotos);
+ return ERR_OK;
+#endif /* IP_REASSEMBLY */
+ }
+
+#if IP_OPTIONS_ALLOWED == 0 /* no support for IP options in the IP header? */
+
+#if LWIP_IGMP
+ /* there is an extra "router alert" option in IGMP messages which we allow for but do not police */
+ if ((iphdr_hlen > IP_HLEN) && (IPH_PROTO(iphdr) != IP_PROTO_IGMP)) {
+#else
+ if (iphdr_hlen > IP_HLEN) {
+#endif /* LWIP_IGMP */
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("IP packet dropped since there were IP options (while IP_OPTIONS_ALLOWED == 0).\n"));
+ pbuf_free(p);
+ IP_STATS_INC(ip.opterr);
+ IP_STATS_INC(ip.drop);
+ /* unsupported protocol feature */
+ MIB2_STATS_INC(mib2.ipinunknownprotos);
+ return ERR_OK;
+ }
+#endif /* IP_OPTIONS_ALLOWED == 0 */
+
+ /* send to upper layers */
+ LWIP_DEBUGF(IP_DEBUG, ("ip4_input: \n"));
+ ip4_debug_print(p);
+ LWIP_DEBUGF(IP_DEBUG, ("ip4_input: p->len %"U16_F" p->tot_len %"U16_F"\n", p->len, p->tot_len));
+
+ ip_data.current_netif = netif;
+ ip_data.current_input_netif = inp;
+ ip_data.current_ip4_header = iphdr;
+ ip_data.current_ip_header_tot_len = IPH_HL(iphdr) * 4;
+
+#if LWIP_RAW
+ /* raw input did not eat the packet? */
+ if (raw_input(p, inp) == 0)
+#endif /* LWIP_RAW */
+ {
+ pbuf_header(p, -(s16_t)iphdr_hlen); /* Move to payload, no check necessary. */
+
+ switch (IPH_PROTO(iphdr)) {
+#if LWIP_UDP
+ case IP_PROTO_UDP:
+#if LWIP_UDPLITE
+ case IP_PROTO_UDPLITE:
+#endif /* LWIP_UDPLITE */
+ MIB2_STATS_INC(mib2.ipindelivers);
+ udp_input(p, inp);
+ break;
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+ case IP_PROTO_TCP:
+ MIB2_STATS_INC(mib2.ipindelivers);
+ tcp_input(p, inp);
+ break;
+#endif /* LWIP_TCP */
+#if LWIP_ICMP
+ case IP_PROTO_ICMP:
+ MIB2_STATS_INC(mib2.ipindelivers);
+ icmp_input(p, inp);
+ break;
+#endif /* LWIP_ICMP */
+#if LWIP_IGMP
+ case IP_PROTO_IGMP:
+ igmp_input(p, inp, ip4_current_dest_addr());
+ break;
+#endif /* LWIP_IGMP */
+ default:
+#if LWIP_ICMP
+ /* send ICMP destination protocol unreachable unless is was a broadcast */
+ if (!ip4_addr_isbroadcast(ip4_current_dest_addr(), netif) &&
+ !ip4_addr_ismulticast(ip4_current_dest_addr())) {
+ pbuf_header_force(p, iphdr_hlen); /* Move to ip header, no check necessary. */
+ p->payload = iphdr;
+ icmp_dest_unreach(p, ICMP_DUR_PROTO);
+ }
+#endif /* LWIP_ICMP */
+ pbuf_free(p);
+
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("Unsupported transport protocol %"U16_F"\n", (u16_t)IPH_PROTO(iphdr)));
+
+ IP_STATS_INC(ip.proterr);
+ IP_STATS_INC(ip.drop);
+ MIB2_STATS_INC(mib2.ipinunknownprotos);
+ }
+ }
+
+ /* @todo: this is not really necessary... */
+ ip_data.current_netif = NULL;
+ ip_data.current_input_netif = NULL;
+ ip_data.current_ip4_header = NULL;
+ ip_data.current_ip_header_tot_len = 0;
+ ip4_addr_set_any(ip4_current_src_addr());
+ ip4_addr_set_any(ip4_current_dest_addr());
+
+ return ERR_OK;
+}
+
+/**
+ * Sends an IP packet on a network interface. This function constructs
+ * the IP header and calculates the IP header checksum. If the source
+ * IP address is NULL, the IP address of the outgoing network
+ * interface is filled in as source address.
+ * If the destination IP address is LWIP_IP_HDRINCL, p is assumed to already
+ * include an IP header and p->payload points to it instead of the data.
+ *
+ * @param p the packet to send (p->payload points to the data, e.g. next
+ protocol header; if dest == LWIP_IP_HDRINCL, p already includes an
+ IP header and p->payload points to that IP header)
+ * @param src the source IP address to send from (if src == IP4_ADDR_ANY, the
+ * IP address of the netif used to send is used as source address)
+ * @param dest the destination IP address to send the packet to
+ * @param ttl the TTL value to be set in the IP header
+ * @param tos the TOS value to be set in the IP header
+ * @param proto the PROTOCOL to be set in the IP header
+ * @param netif the netif on which to send this packet
+ * @return ERR_OK if the packet was sent OK
+ * ERR_BUF if p doesn't have enough space for IP/LINK headers
+ * returns errors returned by netif->output
+ *
+ * @note ip_id: RFC791 "some host may be able to simply use
+ * unique identifiers independent of destination"
+ */
+err_t
+ip4_output_if(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest,
+ u8_t ttl, u8_t tos,
+ u8_t proto, struct netif *netif)
+{
+#if IP_OPTIONS_SEND
+ return ip4_output_if_opt(p, src, dest, ttl, tos, proto, netif, NULL, 0);
+}
+
+/**
+ * Same as ip_output_if() but with the possibility to include IP options:
+ *
+ * @ param ip_options pointer to the IP options, copied into the IP header
+ * @ param optlen length of ip_options
+ */
+err_t
+ip4_output_if_opt(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest,
+ u8_t ttl, u8_t tos, u8_t proto, struct netif *netif, void *ip_options,
+ u16_t optlen)
+{
+#endif /* IP_OPTIONS_SEND */
+ const ip4_addr_t *src_used = src;
+ if (dest != LWIP_IP_HDRINCL) {
+ if (ip4_addr_isany(src)) {
+ src_used = netif_ip4_addr(netif);
+ }
+ }
+
+#if IP_OPTIONS_SEND
+ return ip4_output_if_opt_src(p, src_used, dest, ttl, tos, proto, netif,
+ ip_options, optlen);
+#else /* IP_OPTIONS_SEND */
+ return ip4_output_if_src(p, src_used, dest, ttl, tos, proto, netif);
+#endif /* IP_OPTIONS_SEND */
+}
+
+/**
+ * Same as ip_output_if() but 'src' address is not replaced by netif address
+ * when it is 'any'.
+ */
+err_t
+ip4_output_if_src(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest,
+ u8_t ttl, u8_t tos,
+ u8_t proto, struct netif *netif)
+{
+#if IP_OPTIONS_SEND
+ return ip4_output_if_opt_src(p, src, dest, ttl, tos, proto, netif, NULL, 0);
+}
+
+/**
+ * Same as ip_output_if_opt() but 'src' address is not replaced by netif address
+ * when it is 'any'.
+ */
+err_t
+ip4_output_if_opt_src(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest,
+ u8_t ttl, u8_t tos, u8_t proto, struct netif *netif, void *ip_options,
+ u16_t optlen)
+{
+#endif /* IP_OPTIONS_SEND */
+ struct ip_hdr *iphdr;
+ ip4_addr_t dest_addr;
+#if CHECKSUM_GEN_IP_INLINE
+ u32_t chk_sum = 0;
+#endif /* CHECKSUM_GEN_IP_INLINE */
+
+ LWIP_IP_CHECK_PBUF_REF_COUNT_FOR_TX(p);
+
+ MIB2_STATS_INC(mib2.ipoutrequests);
+
+ /* Should the IP header be generated or is it already included in p? */
+ if (dest != LWIP_IP_HDRINCL) {
+ u16_t ip_hlen = IP_HLEN;
+#if IP_OPTIONS_SEND
+ u16_t optlen_aligned = 0;
+ if (optlen != 0) {
+#if CHECKSUM_GEN_IP_INLINE
+ int i;
+#endif /* CHECKSUM_GEN_IP_INLINE */
+ /* round up to a multiple of 4 */
+ optlen_aligned = ((optlen + 3) & ~3);
+ ip_hlen += optlen_aligned;
+ /* First write in the IP options */
+ if (pbuf_header(p, optlen_aligned)) {
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip4_output_if_opt: not enough room for IP options in pbuf\n"));
+ IP_STATS_INC(ip.err);
+ MIB2_STATS_INC(mib2.ipoutdiscards);
+ return ERR_BUF;
+ }
+ MEMCPY(p->payload, ip_options, optlen);
+ if (optlen < optlen_aligned) {
+ /* zero the remaining bytes */
+ memset(((char*)p->payload) + optlen, 0, optlen_aligned - optlen);
+ }
+#if CHECKSUM_GEN_IP_INLINE
+ for (i = 0; i < optlen_aligned/2; i++) {
+ chk_sum += ((u16_t*)p->payload)[i];
+ }
+#endif /* CHECKSUM_GEN_IP_INLINE */
+ }
+#endif /* IP_OPTIONS_SEND */
+ /* generate IP header */
+ if (pbuf_header(p, IP_HLEN)) {
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip4_output: not enough room for IP header in pbuf\n"));
+
+ IP_STATS_INC(ip.err);
+ MIB2_STATS_INC(mib2.ipoutdiscards);
+ return ERR_BUF;
+ }
+
+ iphdr = (struct ip_hdr *)p->payload;
+ LWIP_ASSERT("check that first pbuf can hold struct ip_hdr",
+ (p->len >= sizeof(struct ip_hdr)));
+
+ IPH_TTL_SET(iphdr, ttl);
+ IPH_PROTO_SET(iphdr, proto);
+#if CHECKSUM_GEN_IP_INLINE
+ chk_sum += PP_NTOHS(proto | (ttl << 8));
+#endif /* CHECKSUM_GEN_IP_INLINE */
+
+ /* dest cannot be NULL here */
+ ip4_addr_copy(iphdr->dest, *dest);
+#if CHECKSUM_GEN_IP_INLINE
+ chk_sum += ip4_addr_get_u32(&iphdr->dest) & 0xFFFF;
+ chk_sum += ip4_addr_get_u32(&iphdr->dest) >> 16;
+#endif /* CHECKSUM_GEN_IP_INLINE */
+
+ IPH_VHL_SET(iphdr, 4, ip_hlen / 4);
+ IPH_TOS_SET(iphdr, tos);
+#if CHECKSUM_GEN_IP_INLINE
+ chk_sum += PP_NTOHS(tos | (iphdr->_v_hl << 8));
+#endif /* CHECKSUM_GEN_IP_INLINE */
+ IPH_LEN_SET(iphdr, lwip_htons(p->tot_len));
+#if CHECKSUM_GEN_IP_INLINE
+ chk_sum += iphdr->_len;
+#endif /* CHECKSUM_GEN_IP_INLINE */
+ IPH_OFFSET_SET(iphdr, 0);
+ IPH_ID_SET(iphdr, lwip_htons(ip_id));
+#if CHECKSUM_GEN_IP_INLINE
+ chk_sum += iphdr->_id;
+#endif /* CHECKSUM_GEN_IP_INLINE */
+ ++ip_id;
+
+ if (src == NULL) {
+ ip4_addr_copy(iphdr->src, *IP4_ADDR_ANY4);
+ } else {
+ /* src cannot be NULL here */
+ ip4_addr_copy(iphdr->src, *src);
+ }
+
+#if CHECKSUM_GEN_IP_INLINE
+ chk_sum += ip4_addr_get_u32(&iphdr->src) & 0xFFFF;
+ chk_sum += ip4_addr_get_u32(&iphdr->src) >> 16;
+ chk_sum = (chk_sum >> 16) + (chk_sum & 0xFFFF);
+ chk_sum = (chk_sum >> 16) + chk_sum;
+ chk_sum = ~chk_sum;
+ IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_IP) {
+ iphdr->_chksum = (u16_t)chk_sum; /* network order */
+ }
+#if LWIP_CHECKSUM_CTRL_PER_NETIF
+ else {
+ IPH_CHKSUM_SET(iphdr, 0);
+ }
+#endif /* LWIP_CHECKSUM_CTRL_PER_NETIF*/
+#else /* CHECKSUM_GEN_IP_INLINE */
+ IPH_CHKSUM_SET(iphdr, 0);
+#if CHECKSUM_GEN_IP
+ IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_IP) {
+ IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, ip_hlen));
+ }
+#endif /* CHECKSUM_GEN_IP */
+#endif /* CHECKSUM_GEN_IP_INLINE */
+ } else {
+ /* IP header already included in p */
+ iphdr = (struct ip_hdr *)p->payload;
+ ip4_addr_copy(dest_addr, iphdr->dest);
+ dest = &dest_addr;
+ }
+
+ IP_STATS_INC(ip.xmit);
+
+ LWIP_DEBUGF(IP_DEBUG, ("ip4_output_if: %c%c%"U16_F"\n", netif->name[0], netif->name[1], (u16_t)netif->num));
+ ip4_debug_print(p);
+
+#if ENABLE_LOOPBACK
+ if (ip4_addr_cmp(dest, netif_ip4_addr(netif))
+#if !LWIP_HAVE_LOOPIF
+ || ip4_addr_isloopback(dest)
+#endif /* !LWIP_HAVE_LOOPIF */
+ ) {
+ /* Packet to self, enqueue it for loopback */
+ LWIP_DEBUGF(IP_DEBUG, ("netif_loop_output()"));
+ return netif_loop_output(netif, p);
+ }
+#if LWIP_MULTICAST_TX_OPTIONS
+ if ((p->flags & PBUF_FLAG_MCASTLOOP) != 0) {
+ netif_loop_output(netif, p);
+ }
+#endif /* LWIP_MULTICAST_TX_OPTIONS */
+#endif /* ENABLE_LOOPBACK */
+#if IP_FRAG
+ /* don't fragment if interface has mtu set to 0 [loopif] */
+ if (netif->mtu && (p->tot_len > netif->mtu)) {
+ return ip4_frag(p, netif, dest);
+ }
+#endif /* IP_FRAG */
+
+ LWIP_DEBUGF(IP_DEBUG, ("ip4_output_if: call netif->output()\n"));
+ return netif->output(netif, p, dest);
+}
+
+/**
+ * Simple interface to ip_output_if. It finds the outgoing network
+ * interface and calls upon ip_output_if to do the actual work.
+ *
+ * @param p the packet to send (p->payload points to the data, e.g. next
+ protocol header; if dest == LWIP_IP_HDRINCL, p already includes an
+ IP header and p->payload points to that IP header)
+ * @param src the source IP address to send from (if src == IP4_ADDR_ANY, the
+ * IP address of the netif used to send is used as source address)
+ * @param dest the destination IP address to send the packet to
+ * @param ttl the TTL value to be set in the IP header
+ * @param tos the TOS value to be set in the IP header
+ * @param proto the PROTOCOL to be set in the IP header
+ *
+ * @return ERR_RTE if no route is found
+ * see ip_output_if() for more return values
+ */
+err_t
+ip4_output(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest,
+ u8_t ttl, u8_t tos, u8_t proto)
+{
+ struct netif *netif;
+
+ LWIP_IP_CHECK_PBUF_REF_COUNT_FOR_TX(p);
+
+ if ((netif = ip4_route_src(dest, src)) == NULL) {
+ LWIP_DEBUGF(IP_DEBUG, ("ip4_output: No route to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ ip4_addr1_16(dest), ip4_addr2_16(dest), ip4_addr3_16(dest), ip4_addr4_16(dest)));
+ IP_STATS_INC(ip.rterr);
+ return ERR_RTE;
+ }
+
+ return ip4_output_if(p, src, dest, ttl, tos, proto, netif);
+}
+
+#if LWIP_NETIF_HWADDRHINT
+/** Like ip_output, but takes and addr_hint pointer that is passed on to netif->addr_hint
+ * before calling ip_output_if.
+ *
+ * @param p the packet to send (p->payload points to the data, e.g. next
+ protocol header; if dest == LWIP_IP_HDRINCL, p already includes an
+ IP header and p->payload points to that IP header)
+ * @param src the source IP address to send from (if src == IP4_ADDR_ANY, the
+ * IP address of the netif used to send is used as source address)
+ * @param dest the destination IP address to send the packet to
+ * @param ttl the TTL value to be set in the IP header
+ * @param tos the TOS value to be set in the IP header
+ * @param proto the PROTOCOL to be set in the IP header
+ * @param addr_hint address hint pointer set to netif->addr_hint before
+ * calling ip_output_if()
+ *
+ * @return ERR_RTE if no route is found
+ * see ip_output_if() for more return values
+ */
+err_t
+ip4_output_hinted(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest,
+ u8_t ttl, u8_t tos, u8_t proto, u8_t *addr_hint)
+{
+ struct netif *netif;
+ err_t err;
+
+ LWIP_IP_CHECK_PBUF_REF_COUNT_FOR_TX(p);
+
+ if ((netif = ip4_route_src(dest, src)) == NULL) {
+ LWIP_DEBUGF(IP_DEBUG, ("ip4_output: No route to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ ip4_addr1_16(dest), ip4_addr2_16(dest), ip4_addr3_16(dest), ip4_addr4_16(dest)));
+ IP_STATS_INC(ip.rterr);
+ return ERR_RTE;
+ }
+
+ NETIF_SET_HWADDRHINT(netif, addr_hint);
+ err = ip4_output_if(p, src, dest, ttl, tos, proto, netif);
+ NETIF_SET_HWADDRHINT(netif, NULL);
+
+ return err;
+}
+#endif /* LWIP_NETIF_HWADDRHINT*/
+
+#if IP_DEBUG
+/* Print an IP header by using LWIP_DEBUGF
+ * @param p an IP packet, p->payload pointing to the IP header
+ */
+void
+ip4_debug_print(struct pbuf *p)
+{
+ struct ip_hdr *iphdr = (struct ip_hdr *)p->payload;
+
+ LWIP_DEBUGF(IP_DEBUG, ("IP header:\n"));
+ LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n"));
+ LWIP_DEBUGF(IP_DEBUG, ("|%2"S16_F" |%2"S16_F" | 0x%02"X16_F" | %5"U16_F" | (v, hl, tos, len)\n",
+ (u16_t)IPH_V(iphdr),
+ (u16_t)IPH_HL(iphdr),
+ (u16_t)IPH_TOS(iphdr),
+ lwip_ntohs(IPH_LEN(iphdr))));
+ LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n"));
+ LWIP_DEBUGF(IP_DEBUG, ("| %5"U16_F" |%"U16_F"%"U16_F"%"U16_F"| %4"U16_F" | (id, flags, offset)\n",
+ lwip_ntohs(IPH_ID(iphdr)),
+ (u16_t)(lwip_ntohs(IPH_OFFSET(iphdr)) >> 15 & 1),
+ (u16_t)(lwip_ntohs(IPH_OFFSET(iphdr)) >> 14 & 1),
+ (u16_t)(lwip_ntohs(IPH_OFFSET(iphdr)) >> 13 & 1),
+ (u16_t)(lwip_ntohs(IPH_OFFSET(iphdr)) & IP_OFFMASK)));
+ LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n"));
+ LWIP_DEBUGF(IP_DEBUG, ("| %3"U16_F" | %3"U16_F" | 0x%04"X16_F" | (ttl, proto, chksum)\n",
+ (u16_t)IPH_TTL(iphdr),
+ (u16_t)IPH_PROTO(iphdr),
+ lwip_ntohs(IPH_CHKSUM(iphdr))));
+ LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n"));
+ LWIP_DEBUGF(IP_DEBUG, ("| %3"U16_F" | %3"U16_F" | %3"U16_F" | %3"U16_F" | (src)\n",
+ ip4_addr1_16(&iphdr->src),
+ ip4_addr2_16(&iphdr->src),
+ ip4_addr3_16(&iphdr->src),
+ ip4_addr4_16(&iphdr->src)));
+ LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n"));
+ LWIP_DEBUGF(IP_DEBUG, ("| %3"U16_F" | %3"U16_F" | %3"U16_F" | %3"U16_F" | (dest)\n",
+ ip4_addr1_16(&iphdr->dest),
+ ip4_addr2_16(&iphdr->dest),
+ ip4_addr3_16(&iphdr->dest),
+ ip4_addr4_16(&iphdr->dest)));
+ LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n"));
+}
+#endif /* IP_DEBUG */
+
+#endif /* LWIP_IPV4 */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/ipv4/ip4_addr.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/ipv4/ip4_addr.c
new file mode 100644
index 0000000..2d47992
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/ipv4/ip4_addr.c
@@ -0,0 +1,331 @@
+/**
+ * @file
+ * This is the IPv4 address tools implementation.
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV4
+
+#include "lwip/ip_addr.h"
+#include "lwip/netif.h"
+
+/* used by IP4_ADDR_ANY and IP_ADDR_BROADCAST in ip_addr.h */
+const ip_addr_t ip_addr_any = IPADDR4_INIT(IPADDR_ANY);
+const ip_addr_t ip_addr_broadcast = IPADDR4_INIT(IPADDR_BROADCAST);
+
+/**
+ * Determine if an address is a broadcast address on a network interface
+ *
+ * @param addr address to be checked
+ * @param netif the network interface against which the address is checked
+ * @return returns non-zero if the address is a broadcast address
+ */
+u8_t
+ip4_addr_isbroadcast_u32(u32_t addr, const struct netif *netif)
+{
+ ip4_addr_t ipaddr;
+ ip4_addr_set_u32(&ipaddr, addr);
+
+ /* all ones (broadcast) or all zeroes (old skool broadcast) */
+ if ((~addr == IPADDR_ANY) ||
+ (addr == IPADDR_ANY)) {
+ return 1;
+ /* no broadcast support on this network interface? */
+ } else if ((netif->flags & NETIF_FLAG_BROADCAST) == 0) {
+ /* the given address cannot be a broadcast address
+ * nor can we check against any broadcast addresses */
+ return 0;
+ /* address matches network interface address exactly? => no broadcast */
+ } else if (addr == ip4_addr_get_u32(netif_ip4_addr(netif))) {
+ return 0;
+ /* on the same (sub) network... */
+ } else if (ip4_addr_netcmp(&ipaddr, netif_ip4_addr(netif), netif_ip4_netmask(netif))
+ /* ...and host identifier bits are all ones? =>... */
+ && ((addr & ~ip4_addr_get_u32(netif_ip4_netmask(netif))) ==
+ (IPADDR_BROADCAST & ~ip4_addr_get_u32(netif_ip4_netmask(netif))))) {
+ /* => network broadcast address */
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+/** Checks if a netmask is valid (starting with ones, then only zeros)
+ *
+ * @param netmask the IPv4 netmask to check (in network byte order!)
+ * @return 1 if the netmask is valid, 0 if it is not
+ */
+u8_t
+ip4_addr_netmask_valid(u32_t netmask)
+{
+ u32_t mask;
+ u32_t nm_hostorder = lwip_htonl(netmask);
+
+ /* first, check for the first zero */
+ for (mask = 1UL << 31 ; mask != 0; mask >>= 1) {
+ if ((nm_hostorder & mask) == 0) {
+ break;
+ }
+ }
+ /* then check that there is no one */
+ for (; mask != 0; mask >>= 1) {
+ if ((nm_hostorder & mask) != 0) {
+ /* there is a one after the first zero -> invalid */
+ return 0;
+ }
+ }
+ /* no one after the first zero -> valid */
+ return 1;
+}
+
+/* Here for now until needed in other places in lwIP */
+#ifndef isprint
+#define in_range(c, lo, up) ((u8_t)c >= lo && (u8_t)c <= up)
+#define isprint(c) in_range(c, 0x20, 0x7f)
+#define isdigit(c) in_range(c, '0', '9')
+#define isxdigit(c) (isdigit(c) || in_range(c, 'a', 'f') || in_range(c, 'A', 'F'))
+#define islower(c) in_range(c, 'a', 'z')
+#define isspace(c) (c == ' ' || c == '\f' || c == '\n' || c == '\r' || c == '\t' || c == '\v')
+#endif
+
+/**
+ * Ascii internet address interpretation routine.
+ * The value returned is in network order.
+ *
+ * @param cp IP address in ascii representation (e.g. "127.0.0.1")
+ * @return ip address in network order
+ */
+u32_t
+ipaddr_addr(const char *cp)
+{
+ ip4_addr_t val;
+
+ if (ip4addr_aton(cp, &val)) {
+ return ip4_addr_get_u32(&val);
+ }
+ return (IPADDR_NONE);
+}
+
+/**
+ * Check whether "cp" is a valid ascii representation
+ * of an Internet address and convert to a binary address.
+ * Returns 1 if the address is valid, 0 if not.
+ * This replaces inet_addr, the return value from which
+ * cannot distinguish between failure and a local broadcast address.
+ *
+ * @param cp IP address in ascii representation (e.g. "127.0.0.1")
+ * @param addr pointer to which to save the ip address in network order
+ * @return 1 if cp could be converted to addr, 0 on failure
+ */
+int
+ip4addr_aton(const char *cp, ip4_addr_t *addr)
+{
+ u32_t val;
+ u8_t base;
+ char c;
+ u32_t parts[4];
+ u32_t *pp = parts;
+
+ c = *cp;
+ for (;;) {
+ /*
+ * Collect number up to ``.''.
+ * Values are specified as for C:
+ * 0x=hex, 0=octal, 1-9=decimal.
+ */
+ if (!isdigit(c)) {
+ return 0;
+ }
+ val = 0;
+ base = 10;
+ if (c == '0') {
+ c = *++cp;
+ if (c == 'x' || c == 'X') {
+ base = 16;
+ c = *++cp;
+ } else {
+ base = 8;
+ }
+ }
+ for (;;) {
+ if (isdigit(c)) {
+ val = (val * base) + (u32_t)(c - '0');
+ c = *++cp;
+ } else if (base == 16 && isxdigit(c)) {
+ val = (val << 4) | (u32_t)(c + 10 - (islower(c) ? 'a' : 'A'));
+ c = *++cp;
+ } else {
+ break;
+ }
+ }
+ if (c == '.') {
+ /*
+ * Internet format:
+ * a.b.c.d
+ * a.b.c (with c treated as 16 bits)
+ * a.b (with b treated as 24 bits)
+ */
+ if (pp >= parts + 3) {
+ return 0;
+ }
+ *pp++ = val;
+ c = *++cp;
+ } else {
+ break;
+ }
+ }
+ /*
+ * Check for trailing characters.
+ */
+ if (c != '\0' && !isspace(c)) {
+ return 0;
+ }
+ /*
+ * Concoct the address according to
+ * the number of parts specified.
+ */
+ switch (pp - parts + 1) {
+
+ case 0:
+ return 0; /* initial nondigit */
+
+ case 1: /* a -- 32 bits */
+ break;
+
+ case 2: /* a.b -- 8.24 bits */
+ if (val > 0xffffffUL) {
+ return 0;
+ }
+ if (parts[0] > 0xff) {
+ return 0;
+ }
+ val |= parts[0] << 24;
+ break;
+
+ case 3: /* a.b.c -- 8.8.16 bits */
+ if (val > 0xffff) {
+ return 0;
+ }
+ if ((parts[0] > 0xff) || (parts[1] > 0xff)) {
+ return 0;
+ }
+ val |= (parts[0] << 24) | (parts[1] << 16);
+ break;
+
+ case 4: /* a.b.c.d -- 8.8.8.8 bits */
+ if (val > 0xff) {
+ return 0;
+ }
+ if ((parts[0] > 0xff) || (parts[1] > 0xff) || (parts[2] > 0xff)) {
+ return 0;
+ }
+ val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8);
+ break;
+ default:
+ LWIP_ASSERT("unhandled", 0);
+ break;
+ }
+ if (addr) {
+ ip4_addr_set_u32(addr, lwip_htonl(val));
+ }
+ return 1;
+}
+
+/**
+ * Convert numeric IP address into decimal dotted ASCII representation.
+ * returns ptr to static buffer; not reentrant!
+ *
+ * @param addr ip address in network order to convert
+ * @return pointer to a global static (!) buffer that holds the ASCII
+ * representation of addr
+ */
+char*
+ip4addr_ntoa(const ip4_addr_t *addr)
+{
+ static char str[IP4ADDR_STRLEN_MAX];
+ return ip4addr_ntoa_r(addr, str, IP4ADDR_STRLEN_MAX);
+}
+
+/**
+ * Same as ipaddr_ntoa, but reentrant since a user-supplied buffer is used.
+ *
+ * @param addr ip address in network order to convert
+ * @param buf target buffer where the string is stored
+ * @param buflen length of buf
+ * @return either pointer to buf which now holds the ASCII
+ * representation of addr or NULL if buf was too small
+ */
+char*
+ip4addr_ntoa_r(const ip4_addr_t *addr, char *buf, int buflen)
+{
+ u32_t s_addr;
+ char inv[3];
+ char *rp;
+ u8_t *ap;
+ u8_t rem;
+ u8_t n;
+ u8_t i;
+ int len = 0;
+
+ s_addr = ip4_addr_get_u32(addr);
+
+ rp = buf;
+ ap = (u8_t *)&s_addr;
+ for (n = 0; n < 4; n++) {
+ i = 0;
+ do {
+ rem = *ap % (u8_t)10;
+ *ap /= (u8_t)10;
+ inv[i++] = (char)('0' + rem);
+ } while (*ap);
+ while (i--) {
+ if (len++ >= buflen) {
+ return NULL;
+ }
+ *rp++ = inv[i];
+ }
+ if (len++ >= buflen) {
+ return NULL;
+ }
+ *rp++ = '.';
+ ap++;
+ }
+ *--rp = 0;
+ return buf;
+}
+
+#endif /* LWIP_IPV4 */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/ipv4/ip4_frag.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/ipv4/ip4_frag.c
new file mode 100644
index 0000000..57fb44c
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/ipv4/ip4_frag.c
@@ -0,0 +1,831 @@
+/**
+ * @file
+ * This is the IPv4 packet segmentation and reassembly implementation.
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Jani Monoses <jani@iv.ro>
+ * Simon Goldschmidt
+ * original reassembly code by Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV4
+
+#include "lwip/ip4_frag.h"
+#include "lwip/def.h"
+#include "lwip/inet_chksum.h"
+#include "lwip/netif.h"
+#include "lwip/stats.h"
+#include "lwip/icmp.h"
+
+#include <string.h>
+
+#if IP_REASSEMBLY
+/**
+ * The IP reassembly code currently has the following limitations:
+ * - IP header options are not supported
+ * - fragments must not overlap (e.g. due to different routes),
+ * currently, overlapping or duplicate fragments are thrown away
+ * if IP_REASS_CHECK_OVERLAP=1 (the default)!
+ *
+ * @todo: work with IP header options
+ */
+
+/** Setting this to 0, you can turn off checking the fragments for overlapping
+ * regions. The code gets a little smaller. Only use this if you know that
+ * overlapping won't occur on your network! */
+#ifndef IP_REASS_CHECK_OVERLAP
+#define IP_REASS_CHECK_OVERLAP 1
+#endif /* IP_REASS_CHECK_OVERLAP */
+
+/** Set to 0 to prevent freeing the oldest datagram when the reassembly buffer is
+ * full (IP_REASS_MAX_PBUFS pbufs are enqueued). The code gets a little smaller.
+ * Datagrams will be freed by timeout only. Especially useful when MEMP_NUM_REASSDATA
+ * is set to 1, so one datagram can be reassembled at a time, only. */
+#ifndef IP_REASS_FREE_OLDEST
+#define IP_REASS_FREE_OLDEST 1
+#endif /* IP_REASS_FREE_OLDEST */
+
+#define IP_REASS_FLAG_LASTFRAG 0x01
+
+/** This is a helper struct which holds the starting
+ * offset and the ending offset of this fragment to
+ * easily chain the fragments.
+ * It has the same packing requirements as the IP header, since it replaces
+ * the IP header in memory in incoming fragments (after copying it) to keep
+ * track of the various fragments. (-> If the IP header doesn't need packing,
+ * this struct doesn't need packing, too.)
+ */
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct ip_reass_helper {
+ PACK_STRUCT_FIELD(struct pbuf *next_pbuf);
+ PACK_STRUCT_FIELD(u16_t start);
+ PACK_STRUCT_FIELD(u16_t end);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+#define IP_ADDRESSES_AND_ID_MATCH(iphdrA, iphdrB) \
+ (ip4_addr_cmp(&(iphdrA)->src, &(iphdrB)->src) && \
+ ip4_addr_cmp(&(iphdrA)->dest, &(iphdrB)->dest) && \
+ IPH_ID(iphdrA) == IPH_ID(iphdrB)) ? 1 : 0
+
+/* global variables */
+static struct ip_reassdata *reassdatagrams;
+static u16_t ip_reass_pbufcount;
+
+/* function prototypes */
+static void ip_reass_dequeue_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev);
+static int ip_reass_free_complete_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev);
+
+/**
+ * Reassembly timer base function
+ * for both NO_SYS == 0 and 1 (!).
+ *
+ * Should be called every 1000 msec (defined by IP_TMR_INTERVAL).
+ */
+void
+ip_reass_tmr(void)
+{
+ struct ip_reassdata *r, *prev = NULL;
+
+ r = reassdatagrams;
+ while (r != NULL) {
+ /* Decrement the timer. Once it reaches 0,
+ * clean up the incomplete fragment assembly */
+ if (r->timer > 0) {
+ r->timer--;
+ LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass_tmr: timer dec %"U16_F"\n",(u16_t)r->timer));
+ prev = r;
+ r = r->next;
+ } else {
+ /* reassembly timed out */
+ struct ip_reassdata *tmp;
+ LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass_tmr: timer timed out\n"));
+ tmp = r;
+ /* get the next pointer before freeing */
+ r = r->next;
+ /* free the helper struct and all enqueued pbufs */
+ ip_reass_free_complete_datagram(tmp, prev);
+ }
+ }
+}
+
+/**
+ * Free a datagram (struct ip_reassdata) and all its pbufs.
+ * Updates the total count of enqueued pbufs (ip_reass_pbufcount),
+ * SNMP counters and sends an ICMP time exceeded packet.
+ *
+ * @param ipr datagram to free
+ * @param prev the previous datagram in the linked list
+ * @return the number of pbufs freed
+ */
+static int
+ip_reass_free_complete_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev)
+{
+ u16_t pbufs_freed = 0;
+ u16_t clen;
+ struct pbuf *p;
+ struct ip_reass_helper *iprh;
+
+ LWIP_ASSERT("prev != ipr", prev != ipr);
+ if (prev != NULL) {
+ LWIP_ASSERT("prev->next == ipr", prev->next == ipr);
+ }
+
+ MIB2_STATS_INC(mib2.ipreasmfails);
+#if LWIP_ICMP
+ iprh = (struct ip_reass_helper *)ipr->p->payload;
+ if (iprh->start == 0) {
+ /* The first fragment was received, send ICMP time exceeded. */
+ /* First, de-queue the first pbuf from r->p. */
+ p = ipr->p;
+ ipr->p = iprh->next_pbuf;
+ /* Then, copy the original header into it. */
+ SMEMCPY(p->payload, &ipr->iphdr, IP_HLEN);
+ icmp_time_exceeded(p, ICMP_TE_FRAG);
+ clen = pbuf_clen(p);
+ LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff);
+ pbufs_freed += clen;
+ pbuf_free(p);
+ }
+#endif /* LWIP_ICMP */
+
+ /* First, free all received pbufs. The individual pbufs need to be released
+ separately as they have not yet been chained */
+ p = ipr->p;
+ while (p != NULL) {
+ struct pbuf *pcur;
+ iprh = (struct ip_reass_helper *)p->payload;
+ pcur = p;
+ /* get the next pointer before freeing */
+ p = iprh->next_pbuf;
+ clen = pbuf_clen(pcur);
+ LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff);
+ pbufs_freed += clen;
+ pbuf_free(pcur);
+ }
+ /* Then, unchain the struct ip_reassdata from the list and free it. */
+ ip_reass_dequeue_datagram(ipr, prev);
+ LWIP_ASSERT("ip_reass_pbufcount >= clen", ip_reass_pbufcount >= pbufs_freed);
+ ip_reass_pbufcount -= pbufs_freed;
+
+ return pbufs_freed;
+}
+
+#if IP_REASS_FREE_OLDEST
+/**
+ * Free the oldest datagram to make room for enqueueing new fragments.
+ * The datagram 'fraghdr' belongs to is not freed!
+ *
+ * @param fraghdr IP header of the current fragment
+ * @param pbufs_needed number of pbufs needed to enqueue
+ * (used for freeing other datagrams if not enough space)
+ * @return the number of pbufs freed
+ */
+static int
+ip_reass_remove_oldest_datagram(struct ip_hdr *fraghdr, int pbufs_needed)
+{
+ /* @todo Can't we simply remove the last datagram in the
+ * linked list behind reassdatagrams?
+ */
+ struct ip_reassdata *r, *oldest, *prev, *oldest_prev;
+ int pbufs_freed = 0, pbufs_freed_current;
+ int other_datagrams;
+
+ /* Free datagrams until being allowed to enqueue 'pbufs_needed' pbufs,
+ * but don't free the datagram that 'fraghdr' belongs to! */
+ do {
+ oldest = NULL;
+ prev = NULL;
+ oldest_prev = NULL;
+ other_datagrams = 0;
+ r = reassdatagrams;
+ while (r != NULL) {
+ if (!IP_ADDRESSES_AND_ID_MATCH(&r->iphdr, fraghdr)) {
+ /* Not the same datagram as fraghdr */
+ other_datagrams++;
+ if (oldest == NULL) {
+ oldest = r;
+ oldest_prev = prev;
+ } else if (r->timer <= oldest->timer) {
+ /* older than the previous oldest */
+ oldest = r;
+ oldest_prev = prev;
+ }
+ }
+ if (r->next != NULL) {
+ prev = r;
+ }
+ r = r->next;
+ }
+ if (oldest != NULL) {
+ pbufs_freed_current = ip_reass_free_complete_datagram(oldest, oldest_prev);
+ pbufs_freed += pbufs_freed_current;
+ }
+ } while ((pbufs_freed < pbufs_needed) && (other_datagrams > 1));
+ return pbufs_freed;
+}
+#endif /* IP_REASS_FREE_OLDEST */
+
+/**
+ * Enqueues a new fragment into the fragment queue
+ * @param fraghdr points to the new fragments IP hdr
+ * @param clen number of pbufs needed to enqueue (used for freeing other datagrams if not enough space)
+ * @return A pointer to the queue location into which the fragment was enqueued
+ */
+static struct ip_reassdata*
+ip_reass_enqueue_new_datagram(struct ip_hdr *fraghdr, int clen)
+{
+ struct ip_reassdata* ipr;
+#if ! IP_REASS_FREE_OLDEST
+ LWIP_UNUSED_ARG(clen);
+#endif
+
+ /* No matching previous fragment found, allocate a new reassdata struct */
+ ipr = (struct ip_reassdata *)memp_malloc(MEMP_REASSDATA);
+ if (ipr == NULL) {
+#if IP_REASS_FREE_OLDEST
+ if (ip_reass_remove_oldest_datagram(fraghdr, clen) >= clen) {
+ ipr = (struct ip_reassdata *)memp_malloc(MEMP_REASSDATA);
+ }
+ if (ipr == NULL)
+#endif /* IP_REASS_FREE_OLDEST */
+ {
+ IPFRAG_STATS_INC(ip_frag.memerr);
+ LWIP_DEBUGF(IP_REASS_DEBUG,("Failed to alloc reassdata struct\n"));
+ return NULL;
+ }
+ }
+ memset(ipr, 0, sizeof(struct ip_reassdata));
+ ipr->timer = IP_REASS_MAXAGE;
+
+ /* enqueue the new structure to the front of the list */
+ ipr->next = reassdatagrams;
+ reassdatagrams = ipr;
+ /* copy the ip header for later tests and input */
+ /* @todo: no ip options supported? */
+ SMEMCPY(&(ipr->iphdr), fraghdr, IP_HLEN);
+ return ipr;
+}
+
+/**
+ * Dequeues a datagram from the datagram queue. Doesn't deallocate the pbufs.
+ * @param ipr points to the queue entry to dequeue
+ */
+static void
+ip_reass_dequeue_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev)
+{
+ /* dequeue the reass struct */
+ if (reassdatagrams == ipr) {
+ /* it was the first in the list */
+ reassdatagrams = ipr->next;
+ } else {
+ /* it wasn't the first, so it must have a valid 'prev' */
+ LWIP_ASSERT("sanity check linked list", prev != NULL);
+ prev->next = ipr->next;
+ }
+
+ /* now we can free the ip_reassdata struct */
+ memp_free(MEMP_REASSDATA, ipr);
+}
+
+/**
+ * Chain a new pbuf into the pbuf list that composes the datagram. The pbuf list
+ * will grow over time as new pbufs are rx.
+ * Also checks that the datagram passes basic continuity checks (if the last
+ * fragment was received at least once).
+ * @param ipr points to the reassembly state
+ * @param new_p points to the pbuf for the current fragment
+ * @return 0 if invalid, >0 otherwise
+ */
+static int
+ip_reass_chain_frag_into_datagram_and_validate(struct ip_reassdata *ipr, struct pbuf *new_p)
+{
+ struct ip_reass_helper *iprh, *iprh_tmp, *iprh_prev=NULL;
+ struct pbuf *q;
+ u16_t offset, len;
+ struct ip_hdr *fraghdr;
+ int valid = 1;
+
+ /* Extract length and fragment offset from current fragment */
+ fraghdr = (struct ip_hdr*)new_p->payload;
+ len = lwip_ntohs(IPH_LEN(fraghdr)) - IPH_HL(fraghdr) * 4;
+ offset = (lwip_ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) * 8;
+
+ /* overwrite the fragment's ip header from the pbuf with our helper struct,
+ * and setup the embedded helper structure. */
+ /* make sure the struct ip_reass_helper fits into the IP header */
+ LWIP_ASSERT("sizeof(struct ip_reass_helper) <= IP_HLEN",
+ sizeof(struct ip_reass_helper) <= IP_HLEN);
+ iprh = (struct ip_reass_helper*)new_p->payload;
+ iprh->next_pbuf = NULL;
+ iprh->start = offset;
+ iprh->end = offset + len;
+
+ /* Iterate through until we either get to the end of the list (append),
+ * or we find one with a larger offset (insert). */
+ for (q = ipr->p; q != NULL;) {
+ iprh_tmp = (struct ip_reass_helper*)q->payload;
+ if (iprh->start < iprh_tmp->start) {
+ /* the new pbuf should be inserted before this */
+ iprh->next_pbuf = q;
+ if (iprh_prev != NULL) {
+ /* not the fragment with the lowest offset */
+#if IP_REASS_CHECK_OVERLAP
+ if ((iprh->start < iprh_prev->end) || (iprh->end > iprh_tmp->start)) {
+ /* fragment overlaps with previous or following, throw away */
+ goto freepbuf;
+ }
+#endif /* IP_REASS_CHECK_OVERLAP */
+ iprh_prev->next_pbuf = new_p;
+ } else {
+ /* fragment with the lowest offset */
+ ipr->p = new_p;
+ }
+ break;
+ } else if (iprh->start == iprh_tmp->start) {
+ /* received the same datagram twice: no need to keep the datagram */
+ goto freepbuf;
+#if IP_REASS_CHECK_OVERLAP
+ } else if (iprh->start < iprh_tmp->end) {
+ /* overlap: no need to keep the new datagram */
+ goto freepbuf;
+#endif /* IP_REASS_CHECK_OVERLAP */
+ } else {
+ /* Check if the fragments received so far have no holes. */
+ if (iprh_prev != NULL) {
+ if (iprh_prev->end != iprh_tmp->start) {
+ /* There is a fragment missing between the current
+ * and the previous fragment */
+ valid = 0;
+ }
+ }
+ }
+ q = iprh_tmp->next_pbuf;
+ iprh_prev = iprh_tmp;
+ }
+
+ /* If q is NULL, then we made it to the end of the list. Determine what to do now */
+ if (q == NULL) {
+ if (iprh_prev != NULL) {
+ /* this is (for now), the fragment with the highest offset:
+ * chain it to the last fragment */
+#if IP_REASS_CHECK_OVERLAP
+ LWIP_ASSERT("check fragments don't overlap", iprh_prev->end <= iprh->start);
+#endif /* IP_REASS_CHECK_OVERLAP */
+ iprh_prev->next_pbuf = new_p;
+ if (iprh_prev->end != iprh->start) {
+ valid = 0;
+ }
+ } else {
+#if IP_REASS_CHECK_OVERLAP
+ LWIP_ASSERT("no previous fragment, this must be the first fragment!",
+ ipr->p == NULL);
+#endif /* IP_REASS_CHECK_OVERLAP */
+ /* this is the first fragment we ever received for this ip datagram */
+ ipr->p = new_p;
+ }
+ }
+
+ /* At this point, the validation part begins: */
+ /* If we already received the last fragment */
+ if ((ipr->flags & IP_REASS_FLAG_LASTFRAG) != 0) {
+ /* and had no holes so far */
+ if (valid) {
+ /* then check if the rest of the fragments is here */
+ /* Check if the queue starts with the first datagram */
+ if ((ipr->p == NULL) || (((struct ip_reass_helper*)ipr->p->payload)->start != 0)) {
+ valid = 0;
+ } else {
+ /* and check that there are no holes after this datagram */
+ iprh_prev = iprh;
+ q = iprh->next_pbuf;
+ while (q != NULL) {
+ iprh = (struct ip_reass_helper*)q->payload;
+ if (iprh_prev->end != iprh->start) {
+ valid = 0;
+ break;
+ }
+ iprh_prev = iprh;
+ q = iprh->next_pbuf;
+ }
+ /* if still valid, all fragments are received
+ * (because to the MF==0 already arrived */
+ if (valid) {
+ LWIP_ASSERT("sanity check", ipr->p != NULL);
+ LWIP_ASSERT("sanity check",
+ ((struct ip_reass_helper*)ipr->p->payload) != iprh);
+ LWIP_ASSERT("validate_datagram:next_pbuf!=NULL",
+ iprh->next_pbuf == NULL);
+ LWIP_ASSERT("validate_datagram:datagram end!=datagram len",
+ iprh->end == ipr->datagram_len);
+ }
+ }
+ }
+ /* If valid is 0 here, there are some fragments missing in the middle
+ * (since MF == 0 has already arrived). Such datagrams simply time out if
+ * no more fragments are received... */
+ return valid;
+ }
+ /* If we come here, not all fragments were received, yet! */
+ return 0; /* not yet valid! */
+#if IP_REASS_CHECK_OVERLAP
+freepbuf:
+ ip_reass_pbufcount -= pbuf_clen(new_p);
+ pbuf_free(new_p);
+ return 0;
+#endif /* IP_REASS_CHECK_OVERLAP */
+}
+
+/**
+ * Reassembles incoming IP fragments into an IP datagram.
+ *
+ * @param p points to a pbuf chain of the fragment
+ * @return NULL if reassembly is incomplete, ? otherwise
+ */
+struct pbuf *
+ip4_reass(struct pbuf *p)
+{
+ struct pbuf *r;
+ struct ip_hdr *fraghdr;
+ struct ip_reassdata *ipr;
+ struct ip_reass_helper *iprh;
+ u16_t offset, len, clen;
+
+ IPFRAG_STATS_INC(ip_frag.recv);
+ MIB2_STATS_INC(mib2.ipreasmreqds);
+
+ fraghdr = (struct ip_hdr*)p->payload;
+
+ if ((IPH_HL(fraghdr) * 4) != IP_HLEN) {
+ LWIP_DEBUGF(IP_REASS_DEBUG,("ip4_reass: IP options currently not supported!\n"));
+ IPFRAG_STATS_INC(ip_frag.err);
+ goto nullreturn;
+ }
+
+ offset = (lwip_ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) * 8;
+ len = lwip_ntohs(IPH_LEN(fraghdr)) - IPH_HL(fraghdr) * 4;
+
+ /* Check if we are allowed to enqueue more datagrams. */
+ clen = pbuf_clen(p);
+ if ((ip_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS) {
+#if IP_REASS_FREE_OLDEST
+ if (!ip_reass_remove_oldest_datagram(fraghdr, clen) ||
+ ((ip_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS))
+#endif /* IP_REASS_FREE_OLDEST */
+ {
+ /* No datagram could be freed and still too many pbufs enqueued */
+ LWIP_DEBUGF(IP_REASS_DEBUG,("ip4_reass: Overflow condition: pbufct=%d, clen=%d, MAX=%d\n",
+ ip_reass_pbufcount, clen, IP_REASS_MAX_PBUFS));
+ IPFRAG_STATS_INC(ip_frag.memerr);
+ /* @todo: send ICMP time exceeded here? */
+ /* drop this pbuf */
+ goto nullreturn;
+ }
+ }
+
+ /* Look for the datagram the fragment belongs to in the current datagram queue,
+ * remembering the previous in the queue for later dequeueing. */
+ for (ipr = reassdatagrams; ipr != NULL; ipr = ipr->next) {
+ /* Check if the incoming fragment matches the one currently present
+ in the reassembly buffer. If so, we proceed with copying the
+ fragment into the buffer. */
+ if (IP_ADDRESSES_AND_ID_MATCH(&ipr->iphdr, fraghdr)) {
+ LWIP_DEBUGF(IP_REASS_DEBUG, ("ip4_reass: matching previous fragment ID=%"X16_F"\n",
+ lwip_ntohs(IPH_ID(fraghdr))));
+ IPFRAG_STATS_INC(ip_frag.cachehit);
+ break;
+ }
+ }
+
+ if (ipr == NULL) {
+ /* Enqueue a new datagram into the datagram queue */
+ ipr = ip_reass_enqueue_new_datagram(fraghdr, clen);
+ /* Bail if unable to enqueue */
+ if (ipr == NULL) {
+ goto nullreturn;
+ }
+ } else {
+ if (((lwip_ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) == 0) &&
+ ((lwip_ntohs(IPH_OFFSET(&ipr->iphdr)) & IP_OFFMASK) != 0)) {
+ /* ipr->iphdr is not the header from the first fragment, but fraghdr is
+ * -> copy fraghdr into ipr->iphdr since we want to have the header
+ * of the first fragment (for ICMP time exceeded and later, for copying
+ * all options, if supported)*/
+ SMEMCPY(&ipr->iphdr, fraghdr, IP_HLEN);
+ }
+ }
+ /* Track the current number of pbufs current 'in-flight', in order to limit
+ the number of fragments that may be enqueued at any one time */
+ ip_reass_pbufcount += clen;
+
+ /* At this point, we have either created a new entry or pointing
+ * to an existing one */
+
+ /* check for 'no more fragments', and update queue entry*/
+ if ((IPH_OFFSET(fraghdr) & PP_NTOHS(IP_MF)) == 0) {
+ ipr->flags |= IP_REASS_FLAG_LASTFRAG;
+ ipr->datagram_len = offset + len;
+ LWIP_DEBUGF(IP_REASS_DEBUG,
+ ("ip4_reass: last fragment seen, total len %"S16_F"\n",
+ ipr->datagram_len));
+ }
+ /* find the right place to insert this pbuf */
+ /* @todo: trim pbufs if fragments are overlapping */
+ if (ip_reass_chain_frag_into_datagram_and_validate(ipr, p)) {
+ struct ip_reassdata *ipr_prev;
+ /* the totally last fragment (flag more fragments = 0) was received at least
+ * once AND all fragments are received */
+ ipr->datagram_len += IP_HLEN;
+
+ /* save the second pbuf before copying the header over the pointer */
+ r = ((struct ip_reass_helper*)ipr->p->payload)->next_pbuf;
+
+ /* copy the original ip header back to the first pbuf */
+ fraghdr = (struct ip_hdr*)(ipr->p->payload);
+ SMEMCPY(fraghdr, &ipr->iphdr, IP_HLEN);
+ IPH_LEN_SET(fraghdr, lwip_htons(ipr->datagram_len));
+ IPH_OFFSET_SET(fraghdr, 0);
+ IPH_CHKSUM_SET(fraghdr, 0);
+ /* @todo: do we need to set/calculate the correct checksum? */
+#if CHECKSUM_GEN_IP
+ IF__NETIF_CHECKSUM_ENABLED(ip_current_input_netif(), NETIF_CHECKSUM_GEN_IP) {
+ IPH_CHKSUM_SET(fraghdr, inet_chksum(fraghdr, IP_HLEN));
+ }
+#endif /* CHECKSUM_GEN_IP */
+
+ p = ipr->p;
+
+ /* chain together the pbufs contained within the reass_data list. */
+ while (r != NULL) {
+ iprh = (struct ip_reass_helper*)r->payload;
+
+ /* hide the ip header for every succeeding fragment */
+ pbuf_header(r, -IP_HLEN);
+ pbuf_cat(p, r);
+ r = iprh->next_pbuf;
+ }
+
+ /* find the previous entry in the linked list */
+ if (ipr == reassdatagrams) {
+ ipr_prev = NULL;
+ } else {
+ for (ipr_prev = reassdatagrams; ipr_prev != NULL; ipr_prev = ipr_prev->next) {
+ if (ipr_prev->next == ipr) {
+ break;
+ }
+ }
+ }
+
+ /* release the sources allocate for the fragment queue entry */
+ ip_reass_dequeue_datagram(ipr, ipr_prev);
+
+ /* and adjust the number of pbufs currently queued for reassembly. */
+ ip_reass_pbufcount -= pbuf_clen(p);
+
+ MIB2_STATS_INC(mib2.ipreasmoks);
+
+ /* Return the pbuf chain */
+ return p;
+ }
+ /* the datagram is not (yet?) reassembled completely */
+ LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass_pbufcount: %d out\n", ip_reass_pbufcount));
+ return NULL;
+
+nullreturn:
+ LWIP_DEBUGF(IP_REASS_DEBUG,("ip4_reass: nullreturn\n"));
+ IPFRAG_STATS_INC(ip_frag.drop);
+ pbuf_free(p);
+ return NULL;
+}
+#endif /* IP_REASSEMBLY */
+
+#if IP_FRAG
+#if !LWIP_NETIF_TX_SINGLE_PBUF
+/** Allocate a new struct pbuf_custom_ref */
+static struct pbuf_custom_ref*
+ip_frag_alloc_pbuf_custom_ref(void)
+{
+ return (struct pbuf_custom_ref*)memp_malloc(MEMP_FRAG_PBUF);
+}
+
+/** Free a struct pbuf_custom_ref */
+static void
+ip_frag_free_pbuf_custom_ref(struct pbuf_custom_ref* p)
+{
+ LWIP_ASSERT("p != NULL", p != NULL);
+ memp_free(MEMP_FRAG_PBUF, p);
+}
+
+/** Free-callback function to free a 'struct pbuf_custom_ref', called by
+ * pbuf_free. */
+static void
+ipfrag_free_pbuf_custom(struct pbuf *p)
+{
+ struct pbuf_custom_ref *pcr = (struct pbuf_custom_ref*)p;
+ LWIP_ASSERT("pcr != NULL", pcr != NULL);
+ LWIP_ASSERT("pcr == p", (void*)pcr == (void*)p);
+ if (pcr->original != NULL) {
+ pbuf_free(pcr->original);
+ }
+ ip_frag_free_pbuf_custom_ref(pcr);
+}
+#endif /* !LWIP_NETIF_TX_SINGLE_PBUF */
+
+/**
+ * Fragment an IP datagram if too large for the netif.
+ *
+ * Chop the datagram in MTU sized chunks and send them in order
+ * by pointing PBUF_REFs into p.
+ *
+ * @param p ip packet to send
+ * @param netif the netif on which to send
+ * @param dest destination ip address to which to send
+ *
+ * @return ERR_OK if sent successfully, err_t otherwise
+ */
+err_t
+ip4_frag(struct pbuf *p, struct netif *netif, const ip4_addr_t *dest)
+{
+ struct pbuf *rambuf;
+#if !LWIP_NETIF_TX_SINGLE_PBUF
+ struct pbuf *newpbuf;
+ u16_t newpbuflen = 0;
+ u16_t left_to_copy;
+#endif
+ struct ip_hdr *original_iphdr;
+ struct ip_hdr *iphdr;
+ const u16_t nfb = (netif->mtu - IP_HLEN) / 8;
+ u16_t left, fragsize;
+ u16_t ofo;
+ int last;
+ u16_t poff = IP_HLEN;
+ u16_t tmp;
+
+ original_iphdr = (struct ip_hdr *)p->payload;
+ iphdr = original_iphdr;
+ LWIP_ERROR("ip4_frag() does not support IP options", IPH_HL(iphdr) * 4 == IP_HLEN, return ERR_VAL);
+
+ /* Save original offset */
+ tmp = lwip_ntohs(IPH_OFFSET(iphdr));
+ ofo = tmp & IP_OFFMASK;
+ LWIP_ERROR("ip_frag(): MF already set", (tmp & IP_MF) == 0, return ERR_VAL);
+
+ left = p->tot_len - IP_HLEN;
+
+ while (left) {
+ /* Fill this fragment */
+ fragsize = LWIP_MIN(left, nfb * 8);
+
+#if LWIP_NETIF_TX_SINGLE_PBUF
+ rambuf = pbuf_alloc(PBUF_IP, fragsize, PBUF_RAM);
+ if (rambuf == NULL) {
+ goto memerr;
+ }
+ LWIP_ASSERT("this needs a pbuf in one piece!",
+ (rambuf->len == rambuf->tot_len) && (rambuf->next == NULL));
+ poff += pbuf_copy_partial(p, rambuf->payload, fragsize, poff);
+ /* make room for the IP header */
+ if (pbuf_header(rambuf, IP_HLEN)) {
+ pbuf_free(rambuf);
+ goto memerr;
+ }
+ /* fill in the IP header */
+ SMEMCPY(rambuf->payload, original_iphdr, IP_HLEN);
+ iphdr = (struct ip_hdr*)rambuf->payload;
+#else /* LWIP_NETIF_TX_SINGLE_PBUF */
+ /* When not using a static buffer, create a chain of pbufs.
+ * The first will be a PBUF_RAM holding the link and IP header.
+ * The rest will be PBUF_REFs mirroring the pbuf chain to be fragged,
+ * but limited to the size of an mtu.
+ */
+ rambuf = pbuf_alloc(PBUF_LINK, IP_HLEN, PBUF_RAM);
+ if (rambuf == NULL) {
+ goto memerr;
+ }
+ LWIP_ASSERT("this needs a pbuf in one piece!",
+ (p->len >= (IP_HLEN)));
+ SMEMCPY(rambuf->payload, original_iphdr, IP_HLEN);
+ iphdr = (struct ip_hdr *)rambuf->payload;
+
+ left_to_copy = fragsize;
+ while (left_to_copy) {
+ struct pbuf_custom_ref *pcr;
+ u16_t plen = p->len - poff;
+ newpbuflen = LWIP_MIN(left_to_copy, plen);
+ /* Is this pbuf already empty? */
+ if (!newpbuflen) {
+ poff = 0;
+ p = p->next;
+ continue;
+ }
+ pcr = ip_frag_alloc_pbuf_custom_ref();
+ if (pcr == NULL) {
+ pbuf_free(rambuf);
+ goto memerr;
+ }
+ /* Mirror this pbuf, although we might not need all of it. */
+ newpbuf = pbuf_alloced_custom(PBUF_RAW, newpbuflen, PBUF_REF, &pcr->pc,
+ (u8_t*)p->payload + poff, newpbuflen);
+ if (newpbuf == NULL) {
+ ip_frag_free_pbuf_custom_ref(pcr);
+ pbuf_free(rambuf);
+ goto memerr;
+ }
+ pbuf_ref(p);
+ pcr->original = p;
+ pcr->pc.custom_free_function = ipfrag_free_pbuf_custom;
+
+ /* Add it to end of rambuf's chain, but using pbuf_cat, not pbuf_chain
+ * so that it is removed when pbuf_dechain is later called on rambuf.
+ */
+ pbuf_cat(rambuf, newpbuf);
+ left_to_copy -= newpbuflen;
+ if (left_to_copy) {
+ poff = 0;
+ p = p->next;
+ }
+ }
+ poff += newpbuflen;
+#endif /* LWIP_NETIF_TX_SINGLE_PBUF */
+
+ /* Correct header */
+ last = (left <= netif->mtu - IP_HLEN);
+
+ /* Set new offset and MF flag */
+ tmp = (IP_OFFMASK & (ofo));
+ if (!last) {
+ tmp = tmp | IP_MF;
+ }
+ IPH_OFFSET_SET(iphdr, lwip_htons(tmp));
+ IPH_LEN_SET(iphdr, lwip_htons(fragsize + IP_HLEN));
+ IPH_CHKSUM_SET(iphdr, 0);
+#if CHECKSUM_GEN_IP
+ IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_IP) {
+ IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN));
+ }
+#endif /* CHECKSUM_GEN_IP */
+
+ /* No need for separate header pbuf - we allowed room for it in rambuf
+ * when allocated.
+ */
+ netif->output(netif, rambuf, dest);
+ IPFRAG_STATS_INC(ip_frag.xmit);
+
+ /* Unfortunately we can't reuse rambuf - the hardware may still be
+ * using the buffer. Instead we free it (and the ensuing chain) and
+ * recreate it next time round the loop. If we're lucky the hardware
+ * will have already sent the packet, the free will really free, and
+ * there will be zero memory penalty.
+ */
+
+ pbuf_free(rambuf);
+ left -= fragsize;
+ ofo += nfb;
+ }
+ MIB2_STATS_INC(mib2.ipfragoks);
+ return ERR_OK;
+memerr:
+ MIB2_STATS_INC(mib2.ipfragfails);
+ return ERR_MEM;
+}
+#endif /* IP_FRAG */
+
+#endif /* LWIP_IPV4 */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/ipv4/ip_frag.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/ipv4/ip_frag.c
new file mode 100644
index 0000000..c9298aa
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/ipv4/ip_frag.c
@@ -0,0 +1,881 @@
+/**
+ * @file
+ * This is the IPv4 packet segmentation and reassembly implementation.
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Jani Monoses <jani@iv.ro>
+ * Simon Goldschmidt
+ * original reassembly code by Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+#include "lwip/ip_frag.h"
+#include "lwip/def.h"
+#include "lwip/inet_chksum.h"
+#include "lwip/netif.h"
+#include "lwip/snmp.h"
+#include "lwip/stats.h"
+#include "lwip/icmp.h"
+
+#include <string.h>
+
+#if IP_REASSEMBLY
+/**
+ * The IP reassembly code currently has the following limitations:
+ * - IP header options are not supported
+ * - fragments must not overlap (e.g. due to different routes),
+ * currently, overlapping or duplicate fragments are thrown away
+ * if IP_REASS_CHECK_OVERLAP=1 (the default)!
+ *
+ * @todo: work with IP header options
+ */
+
+/** Setting this to 0, you can turn off checking the fragments for overlapping
+ * regions. The code gets a little smaller. Only use this if you know that
+ * overlapping won't occur on your network! */
+#ifndef IP_REASS_CHECK_OVERLAP
+#define IP_REASS_CHECK_OVERLAP 1
+#endif /* IP_REASS_CHECK_OVERLAP */
+
+/** Set to 0 to prevent freeing the oldest datagram when the reassembly buffer is
+ * full (IP_REASS_MAX_PBUFS pbufs are enqueued). The code gets a little smaller.
+ * Datagrams will be freed by timeout only. Especially useful when MEMP_NUM_REASSDATA
+ * is set to 1, so one datagram can be reassembled at a time, only. */
+#ifndef IP_REASS_FREE_OLDEST
+#define IP_REASS_FREE_OLDEST 1
+#endif /* IP_REASS_FREE_OLDEST */
+
+#define IP_REASS_FLAG_LASTFRAG 0x01
+
+/** This is a helper struct which holds the starting
+ * offset and the ending offset of this fragment to
+ * easily chain the fragments.
+ * It has the same packing requirements as the IP header, since it replaces
+ * the IP header in memory in incoming fragments (after copying it) to keep
+ * track of the various fragments. (-> If the IP header doesn't need packing,
+ * this struct doesn't need packing, too.)
+ */
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct ip_reass_helper {
+ PACK_STRUCT_FIELD(struct pbuf *next_pbuf);
+ PACK_STRUCT_FIELD(u16_t start);
+ PACK_STRUCT_FIELD(u16_t end);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+#define IP_ADDRESSES_AND_ID_MATCH(iphdrA, iphdrB) \
+ (ip_addr_cmp(&(iphdrA)->src, &(iphdrB)->src) && \
+ ip_addr_cmp(&(iphdrA)->dest, &(iphdrB)->dest) && \
+ IPH_ID(iphdrA) == IPH_ID(iphdrB)) ? 1 : 0
+
+/* global variables */
+static struct ip_reassdata *reassdatagrams;
+static u16_t ip_reass_pbufcount;
+
+/* function prototypes */
+static void ip_reass_dequeue_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev);
+static int ip_reass_free_complete_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev);
+
+/**
+ * Reassembly timer base function
+ * for both NO_SYS == 0 and 1 (!).
+ *
+ * Should be called every 1000 msec (defined by IP_TMR_INTERVAL).
+ */
+void
+ip_reass_tmr(void)
+{
+ struct ip_reassdata *r, *prev = NULL;
+
+ r = reassdatagrams;
+ while (r != NULL) {
+ /* Decrement the timer. Once it reaches 0,
+ * clean up the incomplete fragment assembly */
+ if (r->timer > 0) {
+ r->timer--;
+ LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass_tmr: timer dec %"U16_F"\n",(u16_t)r->timer));
+ prev = r;
+ r = r->next;
+ } else {
+ /* reassembly timed out */
+ struct ip_reassdata *tmp;
+ LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass_tmr: timer timed out\n"));
+ tmp = r;
+ /* get the next pointer before freeing */
+ r = r->next;
+ /* free the helper struct and all enqueued pbufs */
+ ip_reass_free_complete_datagram(tmp, prev);
+ }
+ }
+}
+
+/**
+ * Free a datagram (struct ip_reassdata) and all its pbufs.
+ * Updates the total count of enqueued pbufs (ip_reass_pbufcount),
+ * SNMP counters and sends an ICMP time exceeded packet.
+ *
+ * @param ipr datagram to free
+ * @param prev the previous datagram in the linked list
+ * @return the number of pbufs freed
+ */
+static int
+ip_reass_free_complete_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev)
+{
+ u16_t pbufs_freed = 0;
+ u8_t clen;
+ struct pbuf *p;
+ struct ip_reass_helper *iprh;
+
+ LWIP_ASSERT("prev != ipr", prev != ipr);
+ if (prev != NULL) {
+ LWIP_ASSERT("prev->next == ipr", prev->next == ipr);
+ }
+
+ snmp_inc_ipreasmfails();
+#if LWIP_ICMP
+ iprh = (struct ip_reass_helper *)ipr->p->payload;
+ if (iprh->start == 0) {
+ /* The first fragment was received, send ICMP time exceeded. */
+ /* First, de-queue the first pbuf from r->p. */
+ p = ipr->p;
+ ipr->p = iprh->next_pbuf;
+ /* Then, copy the original header into it. */
+ SMEMCPY(p->payload, &ipr->iphdr, IP_HLEN);
+ icmp_time_exceeded(p, ICMP_TE_FRAG);
+ clen = pbuf_clen(p);
+ LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff);
+ pbufs_freed += clen;
+ pbuf_free(p);
+ }
+#endif /* LWIP_ICMP */
+
+ /* First, free all received pbufs. The individual pbufs need to be released
+ separately as they have not yet been chained */
+ p = ipr->p;
+ while (p != NULL) {
+ struct pbuf *pcur;
+ iprh = (struct ip_reass_helper *)p->payload;
+ pcur = p;
+ /* get the next pointer before freeing */
+ p = iprh->next_pbuf;
+ clen = pbuf_clen(pcur);
+ LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff);
+ pbufs_freed += clen;
+ pbuf_free(pcur);
+ }
+ /* Then, unchain the struct ip_reassdata from the list and free it. */
+ ip_reass_dequeue_datagram(ipr, prev);
+ LWIP_ASSERT("ip_reass_pbufcount >= clen", ip_reass_pbufcount >= pbufs_freed);
+ ip_reass_pbufcount -= pbufs_freed;
+
+ return pbufs_freed;
+}
+
+#if IP_REASS_FREE_OLDEST
+/**
+ * Free the oldest datagram to make room for enqueueing new fragments.
+ * The datagram 'fraghdr' belongs to is not freed!
+ *
+ * @param fraghdr IP header of the current fragment
+ * @param pbufs_needed number of pbufs needed to enqueue
+ * (used for freeing other datagrams if not enough space)
+ * @return the number of pbufs freed
+ */
+static int
+ip_reass_remove_oldest_datagram(struct ip_hdr *fraghdr, int pbufs_needed)
+{
+ /* @todo Can't we simply remove the last datagram in the
+ * linked list behind reassdatagrams?
+ */
+ struct ip_reassdata *r, *oldest, *prev, *oldest_prev;
+ int pbufs_freed = 0, pbufs_freed_current;
+ int other_datagrams;
+
+ /* Free datagrams until being allowed to enqueue 'pbufs_needed' pbufs,
+ * but don't free the datagram that 'fraghdr' belongs to! */
+ do {
+ oldest = NULL;
+ prev = NULL;
+ oldest_prev = NULL;
+ other_datagrams = 0;
+ r = reassdatagrams;
+ while (r != NULL) {
+ if (!IP_ADDRESSES_AND_ID_MATCH(&r->iphdr, fraghdr)) {
+ /* Not the same datagram as fraghdr */
+ other_datagrams++;
+ if (oldest == NULL) {
+ oldest = r;
+ oldest_prev = prev;
+ } else if (r->timer <= oldest->timer) {
+ /* older than the previous oldest */
+ oldest = r;
+ oldest_prev = prev;
+ }
+ }
+ if (r->next != NULL) {
+ prev = r;
+ }
+ r = r->next;
+ }
+ if (oldest != NULL) {
+ pbufs_freed_current = ip_reass_free_complete_datagram(oldest, oldest_prev);
+ pbufs_freed += pbufs_freed_current;
+ }
+ } while ((pbufs_freed < pbufs_needed) && (other_datagrams > 1));
+ return pbufs_freed;
+}
+#endif /* IP_REASS_FREE_OLDEST */
+
+/**
+ * Enqueues a new fragment into the fragment queue
+ * @param fraghdr points to the new fragments IP hdr
+ * @param clen number of pbufs needed to enqueue (used for freeing other datagrams if not enough space)
+ * @return A pointer to the queue location into which the fragment was enqueued
+ */
+static struct ip_reassdata*
+ip_reass_enqueue_new_datagram(struct ip_hdr *fraghdr, int clen)
+{
+ struct ip_reassdata* ipr;
+ /* No matching previous fragment found, allocate a new reassdata struct */
+ ipr = (struct ip_reassdata *)memp_malloc(MEMP_REASSDATA);
+ if (ipr == NULL) {
+#if IP_REASS_FREE_OLDEST
+ if (ip_reass_remove_oldest_datagram(fraghdr, clen) >= clen) {
+ ipr = (struct ip_reassdata *)memp_malloc(MEMP_REASSDATA);
+ }
+ if (ipr == NULL)
+#endif /* IP_REASS_FREE_OLDEST */
+ {
+ IPFRAG_STATS_INC(ip_frag.memerr);
+ LWIP_DEBUGF(IP_REASS_DEBUG,("Failed to alloc reassdata struct\n"));
+ return NULL;
+ }
+ }
+ memset(ipr, 0, sizeof(struct ip_reassdata));
+ ipr->timer = IP_REASS_MAXAGE;
+
+ /* enqueue the new structure to the front of the list */
+ ipr->next = reassdatagrams;
+ reassdatagrams = ipr;
+ /* copy the ip header for later tests and input */
+ /* @todo: no ip options supported? */
+ SMEMCPY(&(ipr->iphdr), fraghdr, IP_HLEN);
+ return ipr;
+}
+
+/**
+ * Dequeues a datagram from the datagram queue. Doesn't deallocate the pbufs.
+ * @param ipr points to the queue entry to dequeue
+ */
+static void
+ip_reass_dequeue_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev)
+{
+
+ /* dequeue the reass struct */
+ if (reassdatagrams == ipr) {
+ /* it was the first in the list */
+ reassdatagrams = ipr->next;
+ } else {
+ /* it wasn't the first, so it must have a valid 'prev' */
+ LWIP_ASSERT("sanity check linked list", prev != NULL);
+ prev->next = ipr->next;
+ }
+
+ /* now we can free the ip_reass struct */
+ memp_free(MEMP_REASSDATA, ipr);
+}
+
+/**
+ * Chain a new pbuf into the pbuf list that composes the datagram. The pbuf list
+ * will grow over time as new pbufs are rx.
+ * Also checks that the datagram passes basic continuity checks (if the last
+ * fragment was received at least once).
+ * @param root_p points to the 'root' pbuf for the current datagram being assembled.
+ * @param new_p points to the pbuf for the current fragment
+ * @return 0 if invalid, >0 otherwise
+ */
+static int
+ip_reass_chain_frag_into_datagram_and_validate(struct ip_reassdata *ipr, struct pbuf *new_p)
+{
+ struct ip_reass_helper *iprh, *iprh_tmp, *iprh_prev=NULL;
+ struct pbuf *q;
+ u16_t offset,len;
+ struct ip_hdr *fraghdr;
+ int valid = 1;
+
+ /* Extract length and fragment offset from current fragment */
+ fraghdr = (struct ip_hdr*)new_p->payload;
+ len = ntohs(IPH_LEN(fraghdr)) - IPH_HL(fraghdr) * 4;
+ offset = (ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) * 8;
+
+ /* overwrite the fragment's ip header from the pbuf with our helper struct,
+ * and setup the embedded helper structure. */
+ /* make sure the struct ip_reass_helper fits into the IP header */
+ LWIP_ASSERT("sizeof(struct ip_reass_helper) <= IP_HLEN",
+ sizeof(struct ip_reass_helper) <= IP_HLEN);
+ iprh = (struct ip_reass_helper*)new_p->payload;
+ iprh->next_pbuf = NULL;
+ iprh->start = offset;
+ iprh->end = offset + len;
+
+ /* Iterate through until we either get to the end of the list (append),
+ * or we find on with a larger offset (insert). */
+ for (q = ipr->p; q != NULL;) {
+ iprh_tmp = (struct ip_reass_helper*)q->payload;
+ if (iprh->start < iprh_tmp->start) {
+ /* the new pbuf should be inserted before this */
+ iprh->next_pbuf = q;
+ if (iprh_prev != NULL) {
+ /* not the fragment with the lowest offset */
+#if IP_REASS_CHECK_OVERLAP
+ if ((iprh->start < iprh_prev->end) || (iprh->end > iprh_tmp->start)) {
+ /* fragment overlaps with previous or following, throw away */
+ goto freepbuf;
+ }
+#endif /* IP_REASS_CHECK_OVERLAP */
+ iprh_prev->next_pbuf = new_p;
+ } else {
+ /* fragment with the lowest offset */
+ ipr->p = new_p;
+ }
+ break;
+ } else if(iprh->start == iprh_tmp->start) {
+ /* received the same datagram twice: no need to keep the datagram */
+ goto freepbuf;
+#if IP_REASS_CHECK_OVERLAP
+ } else if(iprh->start < iprh_tmp->end) {
+ /* overlap: no need to keep the new datagram */
+ goto freepbuf;
+#endif /* IP_REASS_CHECK_OVERLAP */
+ } else {
+ /* Check if the fragments received so far have no wholes. */
+ if (iprh_prev != NULL) {
+ if (iprh_prev->end != iprh_tmp->start) {
+ /* There is a fragment missing between the current
+ * and the previous fragment */
+ valid = 0;
+ }
+ }
+ }
+ q = iprh_tmp->next_pbuf;
+ iprh_prev = iprh_tmp;
+ }
+
+ /* If q is NULL, then we made it to the end of the list. Determine what to do now */
+ if (q == NULL) {
+ if (iprh_prev != NULL) {
+ /* this is (for now), the fragment with the highest offset:
+ * chain it to the last fragment */
+#if IP_REASS_CHECK_OVERLAP
+ LWIP_ASSERT("check fragments don't overlap", iprh_prev->end <= iprh->start);
+#endif /* IP_REASS_CHECK_OVERLAP */
+ iprh_prev->next_pbuf = new_p;
+ if (iprh_prev->end != iprh->start) {
+ valid = 0;
+ }
+ } else {
+#if IP_REASS_CHECK_OVERLAP
+ LWIP_ASSERT("no previous fragment, this must be the first fragment!",
+ ipr->p == NULL);
+#endif /* IP_REASS_CHECK_OVERLAP */
+ /* this is the first fragment we ever received for this ip datagram */
+ ipr->p = new_p;
+ }
+ }
+
+ /* At this point, the validation part begins: */
+ /* If we already received the last fragment */
+ if ((ipr->flags & IP_REASS_FLAG_LASTFRAG) != 0) {
+ /* and had no wholes so far */
+ if (valid) {
+ /* then check if the rest of the fragments is here */
+ /* Check if the queue starts with the first datagram */
+ if ((ipr->p == NULL) || (((struct ip_reass_helper*)ipr->p->payload)->start != 0)) {
+ valid = 0;
+ } else {
+ /* and check that there are no wholes after this datagram */
+ iprh_prev = iprh;
+ q = iprh->next_pbuf;
+ while (q != NULL) {
+ iprh = (struct ip_reass_helper*)q->payload;
+ if (iprh_prev->end != iprh->start) {
+ valid = 0;
+ break;
+ }
+ iprh_prev = iprh;
+ q = iprh->next_pbuf;
+ }
+ /* if still valid, all fragments are received
+ * (because to the MF==0 already arrived */
+ if (valid) {
+ LWIP_ASSERT("sanity check", ipr->p != NULL);
+ LWIP_ASSERT("sanity check",
+ ((struct ip_reass_helper*)ipr->p->payload) != iprh);
+ LWIP_ASSERT("validate_datagram:next_pbuf!=NULL",
+ iprh->next_pbuf == NULL);
+ LWIP_ASSERT("validate_datagram:datagram end!=datagram len",
+ iprh->end == ipr->datagram_len);
+ }
+ }
+ }
+ /* If valid is 0 here, there are some fragments missing in the middle
+ * (since MF == 0 has already arrived). Such datagrams simply time out if
+ * no more fragments are received... */
+ return valid;
+ }
+ /* If we come here, not all fragments were received, yet! */
+ return 0; /* not yet valid! */
+#if IP_REASS_CHECK_OVERLAP
+freepbuf:
+ ip_reass_pbufcount -= pbuf_clen(new_p);
+ pbuf_free(new_p);
+ return 0;
+#endif /* IP_REASS_CHECK_OVERLAP */
+}
+
+/**
+ * Reassembles incoming IP fragments into an IP datagram.
+ *
+ * @param p points to a pbuf chain of the fragment
+ * @return NULL if reassembly is incomplete, ? otherwise
+ */
+struct pbuf *
+ip_reass(struct pbuf *p)
+{
+ struct pbuf *r;
+ struct ip_hdr *fraghdr;
+ struct ip_reassdata *ipr;
+ struct ip_reass_helper *iprh;
+ u16_t offset, len;
+ u8_t clen;
+
+ IPFRAG_STATS_INC(ip_frag.recv);
+ snmp_inc_ipreasmreqds();
+
+ fraghdr = (struct ip_hdr*)p->payload;
+
+ if ((IPH_HL(fraghdr) * 4) != IP_HLEN) {
+ LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass: IP options currently not supported!\n"));
+ IPFRAG_STATS_INC(ip_frag.err);
+ goto nullreturn;
+ }
+
+ offset = (ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) * 8;
+ len = ntohs(IPH_LEN(fraghdr)) - IPH_HL(fraghdr) * 4;
+
+ /* Check if we are allowed to enqueue more datagrams. */
+ clen = pbuf_clen(p);
+ if ((ip_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS) {
+#if IP_REASS_FREE_OLDEST
+ if (!ip_reass_remove_oldest_datagram(fraghdr, clen) ||
+ ((ip_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS))
+#endif /* IP_REASS_FREE_OLDEST */
+ {
+ /* No datagram could be freed and still too many pbufs enqueued */
+ LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass: Overflow condition: pbufct=%d, clen=%d, MAX=%d\n",
+ ip_reass_pbufcount, clen, IP_REASS_MAX_PBUFS));
+ IPFRAG_STATS_INC(ip_frag.memerr);
+ /* @todo: send ICMP time exceeded here? */
+ /* drop this pbuf */
+ goto nullreturn;
+ }
+ }
+
+ /* Look for the datagram the fragment belongs to in the current datagram queue,
+ * remembering the previous in the queue for later dequeueing. */
+ for (ipr = reassdatagrams; ipr != NULL; ipr = ipr->next) {
+ /* Check if the incoming fragment matches the one currently present
+ in the reassembly buffer. If so, we proceed with copying the
+ fragment into the buffer. */
+ if (IP_ADDRESSES_AND_ID_MATCH(&ipr->iphdr, fraghdr)) {
+ LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass: matching previous fragment ID=%"X16_F"\n",
+ ntohs(IPH_ID(fraghdr))));
+ IPFRAG_STATS_INC(ip_frag.cachehit);
+ break;
+ }
+ }
+
+ if (ipr == NULL) {
+ /* Enqueue a new datagram into the datagram queue */
+ ipr = ip_reass_enqueue_new_datagram(fraghdr, clen);
+ /* Bail if unable to enqueue */
+ if(ipr == NULL) {
+ goto nullreturn;
+ }
+ } else {
+ if (((ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) == 0) &&
+ ((ntohs(IPH_OFFSET(&ipr->iphdr)) & IP_OFFMASK) != 0)) {
+ /* ipr->iphdr is not the header from the first fragment, but fraghdr is
+ * -> copy fraghdr into ipr->iphdr since we want to have the header
+ * of the first fragment (for ICMP time exceeded and later, for copying
+ * all options, if supported)*/
+ SMEMCPY(&ipr->iphdr, fraghdr, IP_HLEN);
+ }
+ }
+ /* Track the current number of pbufs current 'in-flight', in order to limit
+ the number of fragments that may be enqueued at any one time */
+ ip_reass_pbufcount += clen;
+
+ /* At this point, we have either created a new entry or pointing
+ * to an existing one */
+
+ /* check for 'no more fragments', and update queue entry*/
+ if ((IPH_OFFSET(fraghdr) & PP_NTOHS(IP_MF)) == 0) {
+ ipr->flags |= IP_REASS_FLAG_LASTFRAG;
+ ipr->datagram_len = offset + len;
+ LWIP_DEBUGF(IP_REASS_DEBUG,
+ ("ip_reass: last fragment seen, total len %"S16_F"\n",
+ ipr->datagram_len));
+ }
+ /* find the right place to insert this pbuf */
+ /* @todo: trim pbufs if fragments are overlapping */
+ if (ip_reass_chain_frag_into_datagram_and_validate(ipr, p)) {
+ struct ip_reassdata *ipr_prev;
+ /* the totally last fragment (flag more fragments = 0) was received at least
+ * once AND all fragments are received */
+ ipr->datagram_len += IP_HLEN;
+
+ /* save the second pbuf before copying the header over the pointer */
+ r = ((struct ip_reass_helper*)ipr->p->payload)->next_pbuf;
+
+ /* copy the original ip header back to the first pbuf */
+ fraghdr = (struct ip_hdr*)(ipr->p->payload);
+ SMEMCPY(fraghdr, &ipr->iphdr, IP_HLEN);
+ IPH_LEN_SET(fraghdr, htons(ipr->datagram_len));
+ IPH_OFFSET_SET(fraghdr, 0);
+ IPH_CHKSUM_SET(fraghdr, 0);
+ /* @todo: do we need to set calculate the correct checksum? */
+#if CHECKSUM_GEN_IP
+ IPH_CHKSUM_SET(fraghdr, inet_chksum(fraghdr, IP_HLEN));
+#endif /* CHECKSUM_GEN_IP */
+
+ p = ipr->p;
+
+ /* chain together the pbufs contained within the reass_data list. */
+ while(r != NULL) {
+ iprh = (struct ip_reass_helper*)r->payload;
+
+ /* hide the ip header for every succeeding fragment */
+ pbuf_header(r, -IP_HLEN);
+ pbuf_cat(p, r);
+ r = iprh->next_pbuf;
+ }
+
+ /* find the previous entry in the linked list */
+ if (ipr == reassdatagrams) {
+ ipr_prev = NULL;
+ } else {
+ for (ipr_prev = reassdatagrams; ipr_prev != NULL; ipr_prev = ipr_prev->next) {
+ if (ipr_prev->next == ipr) {
+ break;
+ }
+ }
+ }
+
+ /* release the sources allocate for the fragment queue entry */
+ ip_reass_dequeue_datagram(ipr, ipr_prev);
+
+ /* and adjust the number of pbufs currently queued for reassembly. */
+ ip_reass_pbufcount -= pbuf_clen(p);
+
+ /* Return the pbuf chain */
+ return p;
+ }
+ /* the datagram is not (yet?) reassembled completely */
+ LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass_pbufcount: %d out\n", ip_reass_pbufcount));
+ return NULL;
+
+nullreturn:
+ LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass: nullreturn\n"));
+ IPFRAG_STATS_INC(ip_frag.drop);
+ pbuf_free(p);
+ return NULL;
+}
+#endif /* IP_REASSEMBLY */
+
+#if IP_FRAG
+#if IP_FRAG_USES_STATIC_BUF
+static u8_t buf[LWIP_MEM_ALIGN_SIZE(IP_FRAG_MAX_MTU + MEM_ALIGNMENT - 1)];
+#else /* IP_FRAG_USES_STATIC_BUF */
+
+#if !LWIP_NETIF_TX_SINGLE_PBUF
+/** Allocate a new struct pbuf_custom_ref */
+static struct pbuf_custom_ref*
+ip_frag_alloc_pbuf_custom_ref(void)
+{
+ return (struct pbuf_custom_ref*)memp_malloc(MEMP_FRAG_PBUF);
+}
+
+/** Free a struct pbuf_custom_ref */
+static void
+ip_frag_free_pbuf_custom_ref(struct pbuf_custom_ref* p)
+{
+ LWIP_ASSERT("p != NULL", p != NULL);
+ memp_free(MEMP_FRAG_PBUF, p);
+}
+
+/** Free-callback function to free a 'struct pbuf_custom_ref', called by
+ * pbuf_free. */
+static void
+ipfrag_free_pbuf_custom(struct pbuf *p)
+{
+ struct pbuf_custom_ref *pcr = (struct pbuf_custom_ref*)p;
+ LWIP_ASSERT("pcr != NULL", pcr != NULL);
+ LWIP_ASSERT("pcr == p", (void*)pcr == (void*)p);
+ if (pcr->original != NULL) {
+ pbuf_free(pcr->original);
+ }
+ ip_frag_free_pbuf_custom_ref(pcr);
+}
+#endif /* !LWIP_NETIF_TX_SINGLE_PBUF */
+#endif /* IP_FRAG_USES_STATIC_BUF */
+
+/**
+ * Fragment an IP datagram if too large for the netif.
+ *
+ * Chop the datagram in MTU sized chunks and send them in order
+ * by using a fixed size static memory buffer (PBUF_REF) or
+ * point PBUF_REFs into p (depending on IP_FRAG_USES_STATIC_BUF).
+ *
+ * @param p ip packet to send
+ * @param netif the netif on which to send
+ * @param dest destination ip address to which to send
+ *
+ * @return ERR_OK if sent successfully, err_t otherwise
+ */
+err_t
+ip_frag(struct pbuf *p, struct netif *netif, const ip_addr_t *dest)
+{
+ struct pbuf *rambuf;
+#if IP_FRAG_USES_STATIC_BUF
+ struct pbuf *header;
+#else
+#if !LWIP_NETIF_TX_SINGLE_PBUF
+ struct pbuf *newpbuf;
+#endif
+ struct ip_hdr *original_iphdr;
+#endif
+ struct ip_hdr *iphdr;
+ u16_t nfb;
+ u16_t left, cop;
+ u16_t mtu = netif->mtu;
+ u16_t ofo, omf;
+ u16_t last;
+ u16_t poff = IP_HLEN;
+ u16_t tmp;
+#if !IP_FRAG_USES_STATIC_BUF && !LWIP_NETIF_TX_SINGLE_PBUF
+ u16_t newpbuflen = 0;
+ u16_t left_to_copy;
+#endif
+
+ /* Get a RAM based MTU sized pbuf */
+#if IP_FRAG_USES_STATIC_BUF
+ /* When using a static buffer, we use a PBUF_REF, which we will
+ * use to reference the packet (without link header).
+ * Layer and length is irrelevant.
+ */
+ rambuf = pbuf_alloc(PBUF_LINK, 0, PBUF_REF);
+ if (rambuf == NULL) {
+ LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_frag: pbuf_alloc(PBUF_LINK, 0, PBUF_REF) failed\n"));
+ return ERR_MEM;
+ }
+ rambuf->tot_len = rambuf->len = mtu;
+ rambuf->payload = LWIP_MEM_ALIGN((void *)buf);
+
+ /* Copy the IP header in it */
+ iphdr = (struct ip_hdr *)rambuf->payload;
+ SMEMCPY(iphdr, p->payload, IP_HLEN);
+#else /* IP_FRAG_USES_STATIC_BUF */
+ original_iphdr = (struct ip_hdr *)p->payload;
+ iphdr = original_iphdr;
+#endif /* IP_FRAG_USES_STATIC_BUF */
+
+ /* Save original offset */
+ tmp = ntohs(IPH_OFFSET(iphdr));
+ ofo = tmp & IP_OFFMASK;
+ omf = tmp & IP_MF;
+
+ left = p->tot_len - IP_HLEN;
+
+ nfb = (mtu - IP_HLEN) / 8;
+
+ while (left) {
+ last = (left <= mtu - IP_HLEN);
+
+ /* Set new offset and MF flag */
+ tmp = omf | (IP_OFFMASK & (ofo));
+ if (!last) {
+ tmp = tmp | IP_MF;
+ }
+
+ /* Fill this fragment */
+ cop = last ? left : nfb * 8;
+
+#if IP_FRAG_USES_STATIC_BUF
+ poff += pbuf_copy_partial(p, (u8_t*)iphdr + IP_HLEN, cop, poff);
+#else /* IP_FRAG_USES_STATIC_BUF */
+#if LWIP_NETIF_TX_SINGLE_PBUF
+ rambuf = pbuf_alloc(PBUF_IP, cop, PBUF_RAM);
+ if (rambuf == NULL) {
+ return ERR_MEM;
+ }
+ LWIP_ASSERT("this needs a pbuf in one piece!",
+ (rambuf->len == rambuf->tot_len) && (rambuf->next == NULL));
+ poff += pbuf_copy_partial(p, rambuf->payload, cop, poff);
+ /* make room for the IP header */
+ if(pbuf_header(rambuf, IP_HLEN)) {
+ pbuf_free(rambuf);
+ return ERR_MEM;
+ }
+ /* fill in the IP header */
+ SMEMCPY(rambuf->payload, original_iphdr, IP_HLEN);
+ iphdr = rambuf->payload;
+#else /* LWIP_NETIF_TX_SINGLE_PBUF */
+ /* When not using a static buffer, create a chain of pbufs.
+ * The first will be a PBUF_RAM holding the link and IP header.
+ * The rest will be PBUF_REFs mirroring the pbuf chain to be fragged,
+ * but limited to the size of an mtu.
+ */
+ rambuf = pbuf_alloc(PBUF_LINK, IP_HLEN, PBUF_RAM);
+ if (rambuf == NULL) {
+ return ERR_MEM;
+ }
+ LWIP_ASSERT("this needs a pbuf in one piece!",
+ (p->len >= (IP_HLEN)));
+ SMEMCPY(rambuf->payload, original_iphdr, IP_HLEN);
+ iphdr = (struct ip_hdr *)rambuf->payload;
+
+ /* Can just adjust p directly for needed offset. */
+ p->payload = (u8_t *)p->payload + poff;
+ p->len -= poff;
+
+ left_to_copy = cop;
+ while (left_to_copy) {
+ struct pbuf_custom_ref *pcr;
+ newpbuflen = (left_to_copy < p->len) ? left_to_copy : p->len;
+ /* Is this pbuf already empty? */
+ if (!newpbuflen) {
+ p = p->next;
+ continue;
+ }
+ pcr = ip_frag_alloc_pbuf_custom_ref();
+ if (pcr == NULL) {
+ pbuf_free(rambuf);
+ return ERR_MEM;
+ }
+ /* Mirror this pbuf, although we might not need all of it. */
+ newpbuf = pbuf_alloced_custom(PBUF_RAW, newpbuflen, PBUF_REF, &pcr->pc, p->payload, newpbuflen);
+ if (newpbuf == NULL) {
+ ip_frag_free_pbuf_custom_ref(pcr);
+ pbuf_free(rambuf);
+ return ERR_MEM;
+ }
+ pbuf_ref(p);
+ pcr->original = p;
+ pcr->pc.custom_free_function = ipfrag_free_pbuf_custom;
+
+ /* Add it to end of rambuf's chain, but using pbuf_cat, not pbuf_chain
+ * so that it is removed when pbuf_dechain is later called on rambuf.
+ */
+ pbuf_cat(rambuf, newpbuf);
+ left_to_copy -= newpbuflen;
+ if (left_to_copy) {
+ p = p->next;
+ }
+ }
+ poff = newpbuflen;
+#endif /* LWIP_NETIF_TX_SINGLE_PBUF */
+#endif /* IP_FRAG_USES_STATIC_BUF */
+
+ /* Correct header */
+ IPH_OFFSET_SET(iphdr, htons(tmp));
+ IPH_LEN_SET(iphdr, htons(cop + IP_HLEN));
+ IPH_CHKSUM_SET(iphdr, 0);
+#if CHECKSUM_GEN_IP
+ IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN));
+#endif /* CHECKSUM_GEN_IP */
+
+#if IP_FRAG_USES_STATIC_BUF
+ if (last) {
+ pbuf_realloc(rambuf, left + IP_HLEN);
+ }
+
+ /* This part is ugly: we alloc a RAM based pbuf for
+ * the link level header for each chunk and then
+ * free it.A PBUF_ROM style pbuf for which pbuf_header
+ * worked would make things simpler.
+ */
+ header = pbuf_alloc(PBUF_LINK, 0, PBUF_RAM);
+ if (header != NULL) {
+ pbuf_chain(header, rambuf);
+ netif->output(netif, header, dest);
+ IPFRAG_STATS_INC(ip_frag.xmit);
+ snmp_inc_ipfragcreates();
+ pbuf_free(header);
+ } else {
+ LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_frag: pbuf_alloc() for header failed\n"));
+ pbuf_free(rambuf);
+ return ERR_MEM;
+ }
+#else /* IP_FRAG_USES_STATIC_BUF */
+ /* No need for separate header pbuf - we allowed room for it in rambuf
+ * when allocated.
+ */
+ netif->output(netif, rambuf, dest);
+ IPFRAG_STATS_INC(ip_frag.xmit);
+
+ /* Unfortunately we can't reuse rambuf - the hardware may still be
+ * using the buffer. Instead we free it (and the ensuing chain) and
+ * recreate it next time round the loop. If we're lucky the hardware
+ * will have already sent the packet, the free will really free, and
+ * there will be zero memory penalty.
+ */
+
+ pbuf_free(rambuf);
+#endif /* IP_FRAG_USES_STATIC_BUF */
+ left -= cop;
+ ofo += nfb;
+ }
+#if IP_FRAG_USES_STATIC_BUF
+ pbuf_free(rambuf);
+#endif /* IP_FRAG_USES_STATIC_BUF */
+ snmp_inc_ipfragoks();
+ return ERR_OK;
+}
+#endif /* IP_FRAG */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/ipv6/README b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/ipv6/README
new file mode 100644
index 0000000..3620004
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/ipv6/README
@@ -0,0 +1 @@
+IPv6 support in lwIP is very experimental.
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/ipv6/dhcp6.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/ipv6/dhcp6.c
new file mode 100644
index 0000000..f27a725
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/ipv6/dhcp6.c
@@ -0,0 +1,50 @@
+/**
+ * @file
+ *
+ * DHCPv6.
+ */
+
+/*
+ * Copyright (c) 2010 Inico Technologies Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Ivan Delamer <delamer@inicotech.com>
+ *
+ *
+ * Please coordinate changes and requests with Ivan Delamer
+ * <delamer@inicotech.com>
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV6 && LWIP_IPV6_DHCP6 /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/ip6_addr.h"
+#include "lwip/def.h"
+
+
+#endif /* LWIP_IPV6 && LWIP_IPV6_DHCP6 */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/ipv6/ethip6.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/ipv6/ethip6.c
new file mode 100644
index 0000000..8f9a91b
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/ipv6/ethip6.c
@@ -0,0 +1,118 @@
+/**
+ * @file
+ *
+ * Ethernet output for IPv6. Uses ND tables for link-layer addressing.
+ */
+
+/*
+ * Copyright (c) 2010 Inico Technologies Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Ivan Delamer <delamer@inicotech.com>
+ *
+ *
+ * Please coordinate changes and requests with Ivan Delamer
+ * <delamer@inicotech.com>
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV6 && LWIP_ETHERNET
+
+#include "lwip/ethip6.h"
+#include "lwip/nd6.h"
+#include "lwip/pbuf.h"
+#include "lwip/ip6.h"
+#include "lwip/ip6_addr.h"
+#include "lwip/inet_chksum.h"
+#include "lwip/netif.h"
+#include "lwip/icmp6.h"
+#include "lwip/prot/ethernet.h"
+#include "netif/ethernet.h"
+
+#include <string.h>
+
+/**
+ * Resolve and fill-in Ethernet address header for outgoing IPv6 packet.
+ *
+ * For IPv6 multicast, corresponding Ethernet addresses
+ * are selected and the packet is transmitted on the link.
+ *
+ * For unicast addresses, ask the ND6 module what to do. It will either let us
+ * send the the packet right away, or queue the packet for later itself, unless
+ * an error occurs.
+ *
+ * @todo anycast addresses
+ *
+ * @param netif The lwIP network interface which the IP packet will be sent on.
+ * @param q The pbuf(s) containing the IP packet to be sent.
+ * @param ip6addr The IP address of the packet destination.
+ *
+ * @return
+ * - ERR_OK or the return value of @ref nd6_get_next_hop_addr_or_queue.
+ */
+err_t
+ethip6_output(struct netif *netif, struct pbuf *q, const ip6_addr_t *ip6addr)
+{
+ struct eth_addr dest;
+ const u8_t *hwaddr;
+ err_t result;
+
+ /* multicast destination IP address? */
+ if (ip6_addr_ismulticast(ip6addr)) {
+ /* Hash IP multicast address to MAC address.*/
+ dest.addr[0] = 0x33;
+ dest.addr[1] = 0x33;
+ dest.addr[2] = ((const u8_t *)(&(ip6addr->addr[3])))[0];
+ dest.addr[3] = ((const u8_t *)(&(ip6addr->addr[3])))[1];
+ dest.addr[4] = ((const u8_t *)(&(ip6addr->addr[3])))[2];
+ dest.addr[5] = ((const u8_t *)(&(ip6addr->addr[3])))[3];
+
+ /* Send out. */
+ return ethernet_output(netif, q, (const struct eth_addr*)(netif->hwaddr), &dest, ETHTYPE_IPV6);
+ }
+
+ /* We have a unicast destination IP address */
+ /* @todo anycast? */
+
+ /* Ask ND6 what to do with the packet. */
+ result = nd6_get_next_hop_addr_or_queue(netif, q, ip6addr, &hwaddr);
+ if (result != ERR_OK) {
+ return result;
+ }
+
+ /* If no hardware address is returned, nd6 has queued the packet for later. */
+ if (hwaddr == NULL) {
+ return ERR_OK;
+ }
+
+ /* Send out the packet using the returned hardware address. */
+ SMEMCPY(dest.addr, hwaddr, 6);
+ return ethernet_output(netif, q, (const struct eth_addr*)(netif->hwaddr), &dest, ETHTYPE_IPV6);
+}
+
+#endif /* LWIP_IPV6 && LWIP_ETHERNET */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/ipv6/icmp6.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/ipv6/icmp6.c
new file mode 100644
index 0000000..323b69a
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/ipv6/icmp6.c
@@ -0,0 +1,350 @@
+/**
+ * @file
+ *
+ * IPv6 version of ICMP, as per RFC 4443.
+ */
+
+/*
+ * Copyright (c) 2010 Inico Technologies Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Ivan Delamer <delamer@inicotech.com>
+ *
+ *
+ * Please coordinate changes and requests with Ivan Delamer
+ * <delamer@inicotech.com>
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_ICMP6 && LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/icmp6.h"
+#include "lwip/prot/icmp6.h"
+#include "lwip/ip6.h"
+#include "lwip/ip6_addr.h"
+#include "lwip/inet_chksum.h"
+#include "lwip/pbuf.h"
+#include "lwip/netif.h"
+#include "lwip/nd6.h"
+#include "lwip/mld6.h"
+#include "lwip/ip.h"
+#include "lwip/stats.h"
+
+#include <string.h>
+
+#ifndef LWIP_ICMP6_DATASIZE
+#define LWIP_ICMP6_DATASIZE 8
+#endif
+#if LWIP_ICMP6_DATASIZE == 0
+#define LWIP_ICMP6_DATASIZE 8
+#endif
+
+/* Forward declarations */
+static void icmp6_send_response(struct pbuf *p, u8_t code, u32_t data, u8_t type);
+
+
+/**
+ * Process an input ICMPv6 message. Called by ip6_input.
+ *
+ * Will generate a reply for echo requests. Other messages are forwarded
+ * to nd6_input, or mld6_input.
+ *
+ * @param p the mld packet, p->payload pointing to the icmpv6 header
+ * @param inp the netif on which this packet was received
+ */
+void
+icmp6_input(struct pbuf *p, struct netif *inp)
+{
+ struct icmp6_hdr *icmp6hdr;
+ struct pbuf *r;
+ const ip6_addr_t *reply_src;
+
+ ICMP6_STATS_INC(icmp6.recv);
+
+ /* Check that ICMPv6 header fits in payload */
+ if (p->len < sizeof(struct icmp6_hdr)) {
+ /* drop short packets */
+ pbuf_free(p);
+ ICMP6_STATS_INC(icmp6.lenerr);
+ ICMP6_STATS_INC(icmp6.drop);
+ return;
+ }
+
+ icmp6hdr = (struct icmp6_hdr *)p->payload;
+
+#if CHECKSUM_CHECK_ICMP6
+ IF__NETIF_CHECKSUM_ENABLED(inp, NETIF_CHECKSUM_CHECK_ICMP6) {
+ if (ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->tot_len, ip6_current_src_addr(),
+ ip6_current_dest_addr()) != 0) {
+ /* Checksum failed */
+ pbuf_free(p);
+ ICMP6_STATS_INC(icmp6.chkerr);
+ ICMP6_STATS_INC(icmp6.drop);
+ return;
+ }
+ }
+#endif /* CHECKSUM_CHECK_ICMP6 */
+
+ switch (icmp6hdr->type) {
+ case ICMP6_TYPE_NA: /* Neighbor advertisement */
+ case ICMP6_TYPE_NS: /* Neighbor solicitation */
+ case ICMP6_TYPE_RA: /* Router advertisement */
+ case ICMP6_TYPE_RD: /* Redirect */
+ case ICMP6_TYPE_PTB: /* Packet too big */
+ nd6_input(p, inp);
+ return;
+ break;
+ case ICMP6_TYPE_RS:
+#if LWIP_IPV6_FORWARD
+ /* @todo implement router functionality */
+#endif
+ break;
+#if LWIP_IPV6_MLD
+ case ICMP6_TYPE_MLQ:
+ case ICMP6_TYPE_MLR:
+ case ICMP6_TYPE_MLD:
+ mld6_input(p, inp);
+ return;
+ break;
+#endif
+ case ICMP6_TYPE_EREQ:
+#if !LWIP_MULTICAST_PING
+ /* multicast destination address? */
+ if (ip6_addr_ismulticast(ip6_current_dest_addr())) {
+ /* drop */
+ pbuf_free(p);
+ ICMP6_STATS_INC(icmp6.drop);
+ return;
+ }
+#endif /* LWIP_MULTICAST_PING */
+
+ /* Allocate reply. */
+ r = pbuf_alloc(PBUF_IP, p->tot_len, PBUF_RAM);
+ if (r == NULL) {
+ /* drop */
+ pbuf_free(p);
+ ICMP6_STATS_INC(icmp6.memerr);
+ return;
+ }
+
+ /* Copy echo request. */
+ if (pbuf_copy(r, p) != ERR_OK) {
+ /* drop */
+ pbuf_free(p);
+ pbuf_free(r);
+ ICMP6_STATS_INC(icmp6.err);
+ return;
+ }
+
+ /* Determine reply source IPv6 address. */
+#if LWIP_MULTICAST_PING
+ if (ip6_addr_ismulticast(ip6_current_dest_addr())) {
+ reply_src = ip_2_ip6(ip6_select_source_address(inp, ip6_current_src_addr()));
+ if (reply_src == NULL) {
+ /* drop */
+ pbuf_free(p);
+ pbuf_free(r);
+ ICMP6_STATS_INC(icmp6.rterr);
+ return;
+ }
+ }
+ else
+#endif /* LWIP_MULTICAST_PING */
+ {
+ reply_src = ip6_current_dest_addr();
+ }
+
+ /* Set fields in reply. */
+ ((struct icmp6_echo_hdr *)(r->payload))->type = ICMP6_TYPE_EREP;
+ ((struct icmp6_echo_hdr *)(r->payload))->chksum = 0;
+#if CHECKSUM_GEN_ICMP6
+ IF__NETIF_CHECKSUM_ENABLED(inp, NETIF_CHECKSUM_GEN_ICMP6) {
+ ((struct icmp6_echo_hdr *)(r->payload))->chksum = ip6_chksum_pseudo(r,
+ IP6_NEXTH_ICMP6, r->tot_len, reply_src, ip6_current_src_addr());
+ }
+#endif /* CHECKSUM_GEN_ICMP6 */
+
+ /* Send reply. */
+ ICMP6_STATS_INC(icmp6.xmit);
+ ip6_output_if(r, reply_src, ip6_current_src_addr(),
+ LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, inp);
+ pbuf_free(r);
+
+ break;
+ default:
+ ICMP6_STATS_INC(icmp6.proterr);
+ ICMP6_STATS_INC(icmp6.drop);
+ break;
+ }
+
+ pbuf_free(p);
+}
+
+
+/**
+ * Send an icmpv6 'destination unreachable' packet.
+ *
+ * @param p the input packet for which the 'unreachable' should be sent,
+ * p->payload pointing to the IPv6 header
+ * @param c ICMPv6 code for the unreachable type
+ */
+void
+icmp6_dest_unreach(struct pbuf *p, enum icmp6_dur_code c)
+{
+ icmp6_send_response(p, c, 0, ICMP6_TYPE_DUR);
+}
+
+/**
+ * Send an icmpv6 'packet too big' packet.
+ *
+ * @param p the input packet for which the 'packet too big' should be sent,
+ * p->payload pointing to the IPv6 header
+ * @param mtu the maximum mtu that we can accept
+ */
+void
+icmp6_packet_too_big(struct pbuf *p, u32_t mtu)
+{
+ icmp6_send_response(p, 0, mtu, ICMP6_TYPE_PTB);
+}
+
+/**
+ * Send an icmpv6 'time exceeded' packet.
+ *
+ * @param p the input packet for which the 'unreachable' should be sent,
+ * p->payload pointing to the IPv6 header
+ * @param c ICMPv6 code for the time exceeded type
+ */
+void
+icmp6_time_exceeded(struct pbuf *p, enum icmp6_te_code c)
+{
+ icmp6_send_response(p, c, 0, ICMP6_TYPE_TE);
+}
+
+/**
+ * Send an icmpv6 'parameter problem' packet.
+ *
+ * @param p the input packet for which the 'param problem' should be sent,
+ * p->payload pointing to the IP header
+ * @param c ICMPv6 code for the param problem type
+ * @param pointer the pointer to the byte where the parameter is found
+ */
+void
+icmp6_param_problem(struct pbuf *p, enum icmp6_pp_code c, u32_t pointer)
+{
+ icmp6_send_response(p, c, pointer, ICMP6_TYPE_PP);
+}
+
+/**
+ * Send an ICMPv6 packet in response to an incoming packet.
+ *
+ * @param p the input packet for which the response should be sent,
+ * p->payload pointing to the IPv6 header
+ * @param code Code of the ICMPv6 header
+ * @param data Additional 32-bit parameter in the ICMPv6 header
+ * @param type Type of the ICMPv6 header
+ */
+static void
+icmp6_send_response(struct pbuf *p, u8_t code, u32_t data, u8_t type)
+{
+ struct pbuf *q;
+ struct icmp6_hdr *icmp6hdr;
+ const ip6_addr_t *reply_src;
+ ip6_addr_t *reply_dest;
+ ip6_addr_t reply_src_local, reply_dest_local;
+ struct ip6_hdr *ip6hdr;
+ struct netif *netif;
+
+ /* ICMPv6 header + IPv6 header + data */
+ q = pbuf_alloc(PBUF_IP, sizeof(struct icmp6_hdr) + IP6_HLEN + LWIP_ICMP6_DATASIZE,
+ PBUF_RAM);
+ if (q == NULL) {
+ LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded: failed to allocate pbuf for ICMPv6 packet.\n"));
+ ICMP6_STATS_INC(icmp6.memerr);
+ return;
+ }
+ LWIP_ASSERT("check that first pbuf can hold icmp 6message",
+ (q->len >= (sizeof(struct icmp6_hdr) + IP6_HLEN + LWIP_ICMP6_DATASIZE)));
+
+ icmp6hdr = (struct icmp6_hdr *)q->payload;
+ icmp6hdr->type = type;
+ icmp6hdr->code = code;
+ icmp6hdr->data = data;
+
+ /* copy fields from original packet */
+ SMEMCPY((u8_t *)q->payload + sizeof(struct icmp6_hdr), (u8_t *)p->payload,
+ IP6_HLEN + LWIP_ICMP6_DATASIZE);
+
+ /* Get the destination address and netif for this ICMP message. */
+ if ((ip_current_netif() == NULL) ||
+ ((code == ICMP6_TE_FRAG) && (type == ICMP6_TYPE_TE))) {
+ /* Special case, as ip6_current_xxx is either NULL, or points
+ * to a different packet than the one that expired.
+ * We must use the addresses that are stored in the expired packet. */
+ ip6hdr = (struct ip6_hdr *)p->payload;
+ /* copy from packed address to aligned address */
+ ip6_addr_copy(reply_dest_local, ip6hdr->src);
+ ip6_addr_copy(reply_src_local, ip6hdr->dest);
+ reply_dest = &reply_dest_local;
+ reply_src = &reply_src_local;
+ netif = ip6_route(reply_src, reply_dest);
+ if (netif == NULL) {
+ /* drop */
+ pbuf_free(q);
+ ICMP6_STATS_INC(icmp6.rterr);
+ return;
+ }
+ }
+ else {
+ netif = ip_current_netif();
+ reply_dest = ip6_current_src_addr();
+
+ /* Select an address to use as source. */
+ reply_src = ip_2_ip6(ip6_select_source_address(netif, reply_dest));
+ if (reply_src == NULL) {
+ /* drop */
+ pbuf_free(q);
+ ICMP6_STATS_INC(icmp6.rterr);
+ return;
+ }
+ }
+
+ /* calculate checksum */
+ icmp6hdr->chksum = 0;
+#if CHECKSUM_GEN_ICMP6
+ IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_ICMP6) {
+ icmp6hdr->chksum = ip6_chksum_pseudo(q, IP6_NEXTH_ICMP6, q->tot_len,
+ reply_src, reply_dest);
+ }
+#endif /* CHECKSUM_GEN_ICMP6 */
+
+ ICMP6_STATS_INC(icmp6.xmit);
+ ip6_output_if(q, reply_src, reply_dest, LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, netif);
+ pbuf_free(q);
+}
+
+#endif /* LWIP_ICMP6 && LWIP_IPV6 */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/ipv6/inet6.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/ipv6/inet6.c
new file mode 100644
index 0000000..d9a992c
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/ipv6/inet6.c
@@ -0,0 +1,53 @@
+/**
+ * @file
+ *
+ * INET v6 addresses.
+ */
+
+/*
+ * Copyright (c) 2010 Inico Technologies Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Ivan Delamer <delamer@inicotech.com>
+ *
+ *
+ * Please coordinate changes and requests with Ivan Delamer
+ * <delamer@inicotech.com>
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV6 && LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/def.h"
+#include "lwip/inet.h"
+
+/** This variable is initialized by the system to contain the wildcard IPv6 address.
+ */
+const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT;
+
+#endif /* LWIP_IPV6 */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/ipv6/ip6.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/ipv6/ip6.c
new file mode 100644
index 0000000..f14e334
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/ipv6/ip6.c
@@ -0,0 +1,1122 @@
+/**
+ * @file
+ *
+ * IPv6 layer.
+ */
+
+/*
+ * Copyright (c) 2010 Inico Technologies Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Ivan Delamer <delamer@inicotech.com>
+ *
+ *
+ * Please coordinate changes and requests with Ivan Delamer
+ * <delamer@inicotech.com>
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/def.h"
+#include "lwip/mem.h"
+#include "lwip/netif.h"
+#include "lwip/ip.h"
+#include "lwip/ip6.h"
+#include "lwip/ip6_addr.h"
+#include "lwip/ip6_frag.h"
+#include "lwip/icmp6.h"
+#include "lwip/raw.h"
+#include "lwip/udp.h"
+#include "lwip/priv/tcp_priv.h"
+#include "lwip/dhcp6.h"
+#include "lwip/nd6.h"
+#include "lwip/mld6.h"
+#include "lwip/debug.h"
+#include "lwip/stats.h"
+
+#ifdef LWIP_HOOK_FILENAME
+#include LWIP_HOOK_FILENAME
+#endif
+
+/**
+ * Finds the appropriate network interface for a given IPv6 address. It tries to select
+ * a netif following a sequence of heuristics:
+ * 1) if there is only 1 netif, return it
+ * 2) if the destination is a link-local address, try to match the src address to a netif.
+ * this is a tricky case because with multiple netifs, link-local addresses only have
+ * meaning within a particular subnet/link.
+ * 3) tries to match the destination subnet to a configured address
+ * 4) tries to find a router
+ * 5) tries to match the source address to the netif
+ * 6) returns the default netif, if configured
+ *
+ * @param src the source IPv6 address, if known
+ * @param dest the destination IPv6 address for which to find the route
+ * @return the netif on which to send to reach dest
+ */
+struct netif *
+ip6_route(const ip6_addr_t *src, const ip6_addr_t *dest)
+{
+ struct netif *netif;
+ s8_t i;
+
+ /* If single netif configuration, fast return. */
+ if ((netif_list != NULL) && (netif_list->next == NULL)) {
+ if (!netif_is_up(netif_list) || !netif_is_link_up(netif_list)) {
+ return NULL;
+ }
+ return netif_list;
+ }
+
+ /* Special processing for link-local addresses. */
+ if (ip6_addr_islinklocal(dest)) {
+ if (ip6_addr_isany(src)) {
+ /* Use default netif, if Up. */
+ if (netif_default == NULL || !netif_is_up(netif_default) ||
+ !netif_is_link_up(netif_default)) {
+ return NULL;
+ }
+ return netif_default;
+ }
+
+ /* Try to find the netif for the source address, checking that link is up. */
+ for (netif = netif_list; netif != NULL; netif = netif->next) {
+ if (!netif_is_up(netif) || !netif_is_link_up(netif)) {
+ continue;
+ }
+ for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+ if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) &&
+ ip6_addr_cmp(src, netif_ip6_addr(netif, i))) {
+ return netif;
+ }
+ }
+ }
+
+ /* netif not found, use default netif, if up */
+ if (netif_default == NULL || !netif_is_up(netif_default) ||
+ !netif_is_link_up(netif_default)) {
+ return NULL;
+ }
+ return netif_default;
+ }
+
+ /* we come here for non-link-local addresses */
+#ifdef LWIP_HOOK_IP6_ROUTE
+ netif = LWIP_HOOK_IP6_ROUTE(src, dest);
+ if (netif != NULL) {
+ return netif;
+ }
+#endif
+
+ /* See if the destination subnet matches a configured address. */
+ for (netif = netif_list; netif != NULL; netif = netif->next) {
+ if (!netif_is_up(netif) || !netif_is_link_up(netif)) {
+ continue;
+ }
+ for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+ if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) &&
+ ip6_addr_netcmp(dest, netif_ip6_addr(netif, i))) {
+ return netif;
+ }
+ }
+ }
+
+ /* Get the netif for a suitable router. */
+ netif = nd6_find_route(dest);
+ if ((netif != NULL) && netif_is_up(netif) && netif_is_link_up(netif)) {
+ return netif;
+ }
+
+ /* try with the netif that matches the source address. */
+ if (!ip6_addr_isany(src)) {
+ for (netif = netif_list; netif != NULL; netif = netif->next) {
+ if (!netif_is_up(netif) || !netif_is_link_up(netif)) {
+ continue;
+ }
+ for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+ if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) &&
+ ip6_addr_cmp(src, netif_ip6_addr(netif, i))) {
+ return netif;
+ }
+ }
+ }
+ }
+
+#if LWIP_NETIF_LOOPBACK && !LWIP_HAVE_LOOPIF
+ /* loopif is disabled, loopback traffic is passed through any netif */
+ if (ip6_addr_isloopback(dest)) {
+ /* don't check for link on loopback traffic */
+ if (netif_default != NULL && netif_is_up(netif_default)) {
+ return netif_default;
+ }
+ /* default netif is not up, just use any netif for loopback traffic */
+ for (netif = netif_list; netif != NULL; netif = netif->next) {
+ if (netif_is_up(netif)) {
+ return netif;
+ }
+ }
+ return NULL;
+ }
+#endif /* LWIP_NETIF_LOOPBACK && !LWIP_HAVE_LOOPIF */
+
+ /* no matching netif found, use default netif, if up */
+ if ((netif_default == NULL) || !netif_is_up(netif_default) || !netif_is_link_up(netif_default)) {
+ return NULL;
+ }
+ return netif_default;
+}
+
+/**
+ * @ingroup ip6
+ * Select the best IPv6 source address for a given destination
+ * IPv6 address. Loosely follows RFC 3484. "Strong host" behavior
+ * is assumed.
+ *
+ * @param netif the netif on which to send a packet
+ * @param dest the destination we are trying to reach
+ * @return the most suitable source address to use, or NULL if no suitable
+ * source address is found
+ */
+const ip_addr_t *
+ip6_select_source_address(struct netif *netif, const ip6_addr_t *dest)
+{
+ const ip_addr_t *src = NULL;
+ u8_t i;
+
+ /* If dest is link-local, choose a link-local source. */
+ if (ip6_addr_islinklocal(dest) || ip6_addr_ismulticast_linklocal(dest) || ip6_addr_ismulticast_iflocal(dest)) {
+ for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+ if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) &&
+ ip6_addr_islinklocal(netif_ip6_addr(netif, i))) {
+ return netif_ip_addr6(netif, i);
+ }
+ }
+ }
+
+ /* Choose a site-local with matching prefix. */
+ if (ip6_addr_issitelocal(dest) || ip6_addr_ismulticast_sitelocal(dest)) {
+ for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+ if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) &&
+ ip6_addr_issitelocal(netif_ip6_addr(netif, i)) &&
+ ip6_addr_netcmp(dest, netif_ip6_addr(netif, i))) {
+ return netif_ip_addr6(netif, i);
+ }
+ }
+ }
+
+ /* Choose a unique-local with matching prefix. */
+ if (ip6_addr_isuniquelocal(dest) || ip6_addr_ismulticast_orglocal(dest)) {
+ for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+ if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) &&
+ ip6_addr_isuniquelocal(netif_ip6_addr(netif, i)) &&
+ ip6_addr_netcmp(dest, netif_ip6_addr(netif, i))) {
+ return netif_ip_addr6(netif, i);
+ }
+ }
+ }
+
+ /* Choose a global with best matching prefix. */
+ if (ip6_addr_isglobal(dest) || ip6_addr_ismulticast_global(dest)) {
+ for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+ if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) &&
+ ip6_addr_isglobal(netif_ip6_addr(netif, i))) {
+ if (src == NULL) {
+ src = netif_ip_addr6(netif, i);
+ }
+ else {
+ /* Replace src only if we find a prefix match. */
+ /* @todo find longest matching prefix. */
+ if ((!(ip6_addr_netcmp(ip_2_ip6(src), dest))) &&
+ ip6_addr_netcmp(netif_ip6_addr(netif, i), dest)) {
+ src = netif_ip_addr6(netif, i);
+ }
+ }
+ }
+ }
+ if (src != NULL) {
+ return src;
+ }
+ }
+
+ /* Last resort: see if arbitrary prefix matches. */
+ for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+ if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) &&
+ ip6_addr_netcmp(dest, netif_ip6_addr(netif, i))) {
+ return netif_ip_addr6(netif, i);
+ }
+ }
+
+ return NULL;
+}
+
+#if LWIP_IPV6_FORWARD
+/**
+ * Forwards an IPv6 packet. It finds an appropriate route for the
+ * packet, decrements the HL value of the packet, and outputs
+ * the packet on the appropriate interface.
+ *
+ * @param p the packet to forward (p->payload points to IP header)
+ * @param iphdr the IPv6 header of the input packet
+ * @param inp the netif on which this packet was received
+ */
+static void
+ip6_forward(struct pbuf *p, struct ip6_hdr *iphdr, struct netif *inp)
+{
+ struct netif *netif;
+
+ /* do not forward link-local or loopback addresses */
+ if (ip6_addr_islinklocal(ip6_current_dest_addr()) ||
+ ip6_addr_isloopback(ip6_current_dest_addr())) {
+ LWIP_DEBUGF(IP6_DEBUG, ("ip6_forward: not forwarding link-local address.\n"));
+ IP6_STATS_INC(ip6.rterr);
+ IP6_STATS_INC(ip6.drop);
+ return;
+ }
+
+ /* Find network interface where to forward this IP packet to. */
+ netif = ip6_route(IP6_ADDR_ANY6, ip6_current_dest_addr());
+ if (netif == NULL) {
+ LWIP_DEBUGF(IP6_DEBUG, ("ip6_forward: no route for %"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F"\n",
+ IP6_ADDR_BLOCK1(ip6_current_dest_addr()),
+ IP6_ADDR_BLOCK2(ip6_current_dest_addr()),
+ IP6_ADDR_BLOCK3(ip6_current_dest_addr()),
+ IP6_ADDR_BLOCK4(ip6_current_dest_addr()),
+ IP6_ADDR_BLOCK5(ip6_current_dest_addr()),
+ IP6_ADDR_BLOCK6(ip6_current_dest_addr()),
+ IP6_ADDR_BLOCK7(ip6_current_dest_addr()),
+ IP6_ADDR_BLOCK8(ip6_current_dest_addr())));
+#if LWIP_ICMP6
+ /* Don't send ICMP messages in response to ICMP messages */
+ if (IP6H_NEXTH(iphdr) != IP6_NEXTH_ICMP6) {
+ icmp6_dest_unreach(p, ICMP6_DUR_NO_ROUTE);
+ }
+#endif /* LWIP_ICMP6 */
+ IP6_STATS_INC(ip6.rterr);
+ IP6_STATS_INC(ip6.drop);
+ return;
+ }
+ /* Do not forward packets onto the same network interface on which
+ * they arrived. */
+ if (netif == inp) {
+ LWIP_DEBUGF(IP6_DEBUG, ("ip6_forward: not bouncing packets back on incoming interface.\n"));
+ IP6_STATS_INC(ip6.rterr);
+ IP6_STATS_INC(ip6.drop);
+ return;
+ }
+
+ /* decrement HL */
+ IP6H_HOPLIM_SET(iphdr, IP6H_HOPLIM(iphdr) - 1);
+ /* send ICMP6 if HL == 0 */
+ if (IP6H_HOPLIM(iphdr) == 0) {
+#if LWIP_ICMP6
+ /* Don't send ICMP messages in response to ICMP messages */
+ if (IP6H_NEXTH(iphdr) != IP6_NEXTH_ICMP6) {
+ icmp6_time_exceeded(p, ICMP6_TE_HL);
+ }
+#endif /* LWIP_ICMP6 */
+ IP6_STATS_INC(ip6.drop);
+ return;
+ }
+
+ if (netif->mtu && (p->tot_len > netif->mtu)) {
+#if LWIP_ICMP6
+ /* Don't send ICMP messages in response to ICMP messages */
+ if (IP6H_NEXTH(iphdr) != IP6_NEXTH_ICMP6) {
+ icmp6_packet_too_big(p, netif->mtu);
+ }
+#endif /* LWIP_ICMP6 */
+ IP6_STATS_INC(ip6.drop);
+ return;
+ }
+
+ LWIP_DEBUGF(IP6_DEBUG, ("ip6_forward: forwarding packet to %"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F"\n",
+ IP6_ADDR_BLOCK1(ip6_current_dest_addr()),
+ IP6_ADDR_BLOCK2(ip6_current_dest_addr()),
+ IP6_ADDR_BLOCK3(ip6_current_dest_addr()),
+ IP6_ADDR_BLOCK4(ip6_current_dest_addr()),
+ IP6_ADDR_BLOCK5(ip6_current_dest_addr()),
+ IP6_ADDR_BLOCK6(ip6_current_dest_addr()),
+ IP6_ADDR_BLOCK7(ip6_current_dest_addr()),
+ IP6_ADDR_BLOCK8(ip6_current_dest_addr())));
+
+ /* transmit pbuf on chosen interface */
+ netif->output_ip6(netif, p, ip6_current_dest_addr());
+ IP6_STATS_INC(ip6.fw);
+ IP6_STATS_INC(ip6.xmit);
+ return;
+}
+#endif /* LWIP_IPV6_FORWARD */
+
+/**
+ * This function is called by the network interface device driver when
+ * an IPv6 packet is received. The function does the basic checks of the
+ * IP header such as packet size being at least larger than the header
+ * size etc. If the packet was not destined for us, the packet is
+ * forwarded (using ip6_forward).
+ *
+ * Finally, the packet is sent to the upper layer protocol input function.
+ *
+ * @param p the received IPv6 packet (p->payload points to IPv6 header)
+ * @param inp the netif on which this packet was received
+ * @return ERR_OK if the packet was processed (could return ERR_* if it wasn't
+ * processed, but currently always returns ERR_OK)
+ */
+err_t
+ip6_input(struct pbuf *p, struct netif *inp)
+{
+ struct ip6_hdr *ip6hdr;
+ struct netif *netif;
+ u8_t nexth;
+ u16_t hlen; /* the current header length */
+ u8_t i;
+#if 0 /*IP_ACCEPT_LINK_LAYER_ADDRESSING*/
+ @todo
+ int check_ip_src=1;
+#endif /* IP_ACCEPT_LINK_LAYER_ADDRESSING */
+
+ IP6_STATS_INC(ip6.recv);
+
+ /* identify the IP header */
+ ip6hdr = (struct ip6_hdr *)p->payload;
+ if (IP6H_V(ip6hdr) != 6) {
+ LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_WARNING, ("IPv6 packet dropped due to bad version number %"U32_F"\n",
+ IP6H_V(ip6hdr)));
+ pbuf_free(p);
+ IP6_STATS_INC(ip6.err);
+ IP6_STATS_INC(ip6.drop);
+ return ERR_OK;
+ }
+
+#ifdef LWIP_HOOK_IP6_INPUT
+ if (LWIP_HOOK_IP6_INPUT(p, inp)) {
+ /* the packet has been eaten */
+ return ERR_OK;
+ }
+#endif
+
+ /* header length exceeds first pbuf length, or ip length exceeds total pbuf length? */
+ if ((IP6_HLEN > p->len) || ((IP6H_PLEN(ip6hdr) + IP6_HLEN) > p->tot_len)) {
+ if (IP6_HLEN > p->len) {
+ LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+ ("IPv6 header (len %"U16_F") does not fit in first pbuf (len %"U16_F"), IP packet dropped.\n",
+ (u16_t)IP6_HLEN, p->len));
+ }
+ if ((IP6H_PLEN(ip6hdr) + IP6_HLEN) > p->tot_len) {
+ LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+ ("IPv6 (plen %"U16_F") is longer than pbuf (len %"U16_F"), IP packet dropped.\n",
+ (u16_t)(IP6H_PLEN(ip6hdr) + IP6_HLEN), p->tot_len));
+ }
+ /* free (drop) packet pbufs */
+ pbuf_free(p);
+ IP6_STATS_INC(ip6.lenerr);
+ IP6_STATS_INC(ip6.drop);
+ return ERR_OK;
+ }
+
+ /* Trim pbuf. This should have been done at the netif layer,
+ * but we'll do it anyway just to be sure that its done. */
+ pbuf_realloc(p, IP6_HLEN + IP6H_PLEN(ip6hdr));
+
+ /* copy IP addresses to aligned ip6_addr_t */
+ ip_addr_copy_from_ip6(ip_data.current_iphdr_dest, ip6hdr->dest);
+ ip_addr_copy_from_ip6(ip_data.current_iphdr_src, ip6hdr->src);
+
+ /* Don't accept virtual IPv4 mapped IPv6 addresses.
+ * Don't accept multicast source addresses. */
+ if (ip6_addr_isipv4mappedipv6(ip_2_ip6(&ip_data.current_iphdr_dest)) ||
+ ip6_addr_isipv4mappedipv6(ip_2_ip6(&ip_data.current_iphdr_src)) ||
+ ip6_addr_ismulticast(ip_2_ip6(&ip_data.current_iphdr_src))) {
+ IP6_STATS_INC(ip6.err);
+ IP6_STATS_INC(ip6.drop);
+ return ERR_OK;
+ }
+
+ /* current header pointer. */
+ ip_data.current_ip6_header = ip6hdr;
+
+ /* In netif, used in case we need to send ICMPv6 packets back. */
+ ip_data.current_netif = inp;
+ ip_data.current_input_netif = inp;
+
+ /* match packet against an interface, i.e. is this packet for us? */
+ if (ip6_addr_ismulticast(ip6_current_dest_addr())) {
+ /* Always joined to multicast if-local and link-local all-nodes group. */
+ if (ip6_addr_isallnodes_iflocal(ip6_current_dest_addr()) ||
+ ip6_addr_isallnodes_linklocal(ip6_current_dest_addr())) {
+ netif = inp;
+ }
+#if LWIP_IPV6_MLD
+ else if (mld6_lookfor_group(inp, ip6_current_dest_addr())) {
+ netif = inp;
+ }
+#else /* LWIP_IPV6_MLD */
+ else if (ip6_addr_issolicitednode(ip6_current_dest_addr())) {
+ /* Filter solicited node packets when MLD is not enabled
+ * (for Neighbor discovery). */
+ netif = NULL;
+ for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+ if (ip6_addr_isvalid(netif_ip6_addr_state(inp, i)) &&
+ ip6_addr_cmp_solicitednode(ip6_current_dest_addr(), netif_ip6_addr(inp, i))) {
+ netif = inp;
+ LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: solicited node packet accepted on interface %c%c\n",
+ netif->name[0], netif->name[1]));
+ break;
+ }
+ }
+ }
+#endif /* LWIP_IPV6_MLD */
+ else {
+ netif = NULL;
+ }
+ } else {
+ /* start trying with inp. if that's not acceptable, start walking the
+ list of configured netifs.
+ 'first' is used as a boolean to mark whether we started walking the list */
+ int first = 1;
+ netif = inp;
+ do {
+ /* interface is up? */
+ if (netif_is_up(netif)) {
+ /* unicast to this interface address? address configured? */
+ for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+ if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) &&
+ ip6_addr_cmp(ip6_current_dest_addr(), netif_ip6_addr(netif, i))) {
+ /* exit outer loop */
+ goto netif_found;
+ }
+ }
+ }
+ if (first) {
+ if (ip6_addr_islinklocal(ip6_current_dest_addr())
+#if !LWIP_NETIF_LOOPBACK || LWIP_HAVE_LOOPIF
+ || ip6_addr_isloopback(ip6_current_dest_addr())
+#endif /* !LWIP_NETIF_LOOPBACK || LWIP_HAVE_LOOPIF */
+ ) {
+ /* Do not match link-local addresses to other netifs. The loopback
+ * address is to be considered link-local and packets to it should be
+ * dropped on other interfaces, as per RFC 4291 Sec. 2.5.3. This
+ * requirement cannot be implemented in the case that loopback
+ * traffic is sent across a non-loopback interface, however.
+ */
+ netif = NULL;
+ break;
+ }
+ first = 0;
+ netif = netif_list;
+ } else {
+ netif = netif->next;
+ }
+ if (netif == inp) {
+ netif = netif->next;
+ }
+ } while (netif != NULL);
+netif_found:
+ LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet accepted on interface %c%c\n",
+ netif ? netif->name[0] : 'X', netif? netif->name[1] : 'X'));
+ }
+
+ /* "::" packet source address? (used in duplicate address detection) */
+ if (ip6_addr_isany(ip6_current_src_addr()) &&
+ (!ip6_addr_issolicitednode(ip6_current_dest_addr()))) {
+ /* packet source is not valid */
+ /* free (drop) packet pbufs */
+ LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with src ANY_ADDRESS dropped\n"));
+ pbuf_free(p);
+ IP6_STATS_INC(ip6.drop);
+ goto ip6_input_cleanup;
+ }
+
+ /* packet not for us? */
+ if (netif == NULL) {
+ /* packet not for us, route or discard */
+ LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_TRACE, ("ip6_input: packet not for us.\n"));
+#if LWIP_IPV6_FORWARD
+ /* non-multicast packet? */
+ if (!ip6_addr_ismulticast(ip6_current_dest_addr())) {
+ /* try to forward IP packet on (other) interfaces */
+ ip6_forward(p, ip6hdr, inp);
+ }
+#endif /* LWIP_IPV6_FORWARD */
+ pbuf_free(p);
+ goto ip6_input_cleanup;
+ }
+
+ /* current netif pointer. */
+ ip_data.current_netif = netif;
+
+ /* Save next header type. */
+ nexth = IP6H_NEXTH(ip6hdr);
+
+ /* Init header length. */
+ hlen = ip_data.current_ip_header_tot_len = IP6_HLEN;
+
+ /* Move to payload. */
+ pbuf_header(p, -IP6_HLEN);
+
+ /* Process known option extension headers, if present. */
+ while (nexth != IP6_NEXTH_NONE)
+ {
+ switch (nexth) {
+ case IP6_NEXTH_HOPBYHOP:
+ LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Hop-by-Hop options header\n"));
+ /* Get next header type. */
+ nexth = *((u8_t *)p->payload);
+
+ /* Get the header length. */
+ hlen = 8 * (1 + *((u8_t *)p->payload + 1));
+ ip_data.current_ip_header_tot_len += hlen;
+
+ /* Skip over this header. */
+ if (hlen > p->len) {
+ LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+ ("IPv6 options header (hlen %"U16_F") does not fit in first pbuf (len %"U16_F"), IPv6 packet dropped.\n",
+ hlen, p->len));
+ /* free (drop) packet pbufs */
+ pbuf_free(p);
+ IP6_STATS_INC(ip6.lenerr);
+ IP6_STATS_INC(ip6.drop);
+ goto ip6_input_cleanup;
+ }
+
+ pbuf_header(p, -(s16_t)hlen);
+ break;
+ case IP6_NEXTH_DESTOPTS:
+ LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Destination options header\n"));
+ /* Get next header type. */
+ nexth = *((u8_t *)p->payload);
+
+ /* Get the header length. */
+ hlen = 8 * (1 + *((u8_t *)p->payload + 1));
+ ip_data.current_ip_header_tot_len += hlen;
+
+ /* Skip over this header. */
+ if (hlen > p->len) {
+ LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+ ("IPv6 options header (hlen %"U16_F") does not fit in first pbuf (len %"U16_F"), IPv6 packet dropped.\n",
+ hlen, p->len));
+ /* free (drop) packet pbufs */
+ pbuf_free(p);
+ IP6_STATS_INC(ip6.lenerr);
+ IP6_STATS_INC(ip6.drop);
+ goto ip6_input_cleanup;
+ }
+
+ pbuf_header(p, -(s16_t)hlen);
+ break;
+ case IP6_NEXTH_ROUTING:
+ LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Routing header\n"));
+ /* Get next header type. */
+ nexth = *((u8_t *)p->payload);
+
+ /* Get the header length. */
+ hlen = 8 * (1 + *((u8_t *)p->payload + 1));
+ ip_data.current_ip_header_tot_len += hlen;
+
+ /* Skip over this header. */
+ if (hlen > p->len) {
+ LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+ ("IPv6 options header (hlen %"U16_F") does not fit in first pbuf (len %"U16_F"), IPv6 packet dropped.\n",
+ hlen, p->len));
+ /* free (drop) packet pbufs */
+ pbuf_free(p);
+ IP6_STATS_INC(ip6.lenerr);
+ IP6_STATS_INC(ip6.drop);
+ goto ip6_input_cleanup;
+ }
+
+ pbuf_header(p, -(s16_t)hlen);
+ break;
+
+ case IP6_NEXTH_FRAGMENT:
+ {
+ struct ip6_frag_hdr *frag_hdr;
+ LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Fragment header\n"));
+
+ frag_hdr = (struct ip6_frag_hdr *)p->payload;
+
+ /* Get next header type. */
+ nexth = frag_hdr->_nexth;
+
+ /* Fragment Header length. */
+ hlen = 8;
+ ip_data.current_ip_header_tot_len += hlen;
+
+ /* Make sure this header fits in current pbuf. */
+ if (hlen > p->len) {
+ LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+ ("IPv6 options header (hlen %"U16_F") does not fit in first pbuf (len %"U16_F"), IPv6 packet dropped.\n",
+ hlen, p->len));
+ /* free (drop) packet pbufs */
+ pbuf_free(p);
+ IP6_FRAG_STATS_INC(ip6_frag.lenerr);
+ IP6_FRAG_STATS_INC(ip6_frag.drop);
+ goto ip6_input_cleanup;
+ }
+
+ /* Offset == 0 and more_fragments == 0? */
+ if ((frag_hdr->_fragment_offset &
+ PP_HTONS(IP6_FRAG_OFFSET_MASK | IP6_FRAG_MORE_FLAG)) == 0) {
+ /* This is a 1-fragment packet, usually a packet that we have
+ * already reassembled. Skip this header anc continue. */
+ pbuf_header(p, -(s16_t)hlen);
+ } else {
+#if LWIP_IPV6_REASS
+
+ /* reassemble the packet */
+ p = ip6_reass(p);
+ /* packet not fully reassembled yet? */
+ if (p == NULL) {
+ goto ip6_input_cleanup;
+ }
+
+ /* Returned p point to IPv6 header.
+ * Update all our variables and pointers and continue. */
+ ip6hdr = (struct ip6_hdr *)p->payload;
+ nexth = IP6H_NEXTH(ip6hdr);
+ hlen = ip_data.current_ip_header_tot_len = IP6_HLEN;
+ pbuf_header(p, -IP6_HLEN);
+
+#else /* LWIP_IPV6_REASS */
+ /* free (drop) packet pbufs */
+ LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Fragment header dropped (with LWIP_IPV6_REASS==0)\n"));
+ pbuf_free(p);
+ IP6_STATS_INC(ip6.opterr);
+ IP6_STATS_INC(ip6.drop);
+ goto ip6_input_cleanup;
+#endif /* LWIP_IPV6_REASS */
+ }
+ break;
+ }
+ default:
+ goto options_done;
+ break;
+ }
+ }
+options_done:
+
+ /* p points to IPv6 header again. */
+ pbuf_header_force(p, (s16_t)ip_data.current_ip_header_tot_len);
+
+ /* send to upper layers */
+ LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: \n"));
+ ip6_debug_print(p);
+ LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: p->len %"U16_F" p->tot_len %"U16_F"\n", p->len, p->tot_len));
+
+#if LWIP_RAW
+ /* raw input did not eat the packet? */
+ if (raw_input(p, inp) == 0)
+#endif /* LWIP_RAW */
+ {
+ switch (nexth) {
+ case IP6_NEXTH_NONE:
+ pbuf_free(p);
+ break;
+#if LWIP_UDP
+ case IP6_NEXTH_UDP:
+#if LWIP_UDPLITE
+ case IP6_NEXTH_UDPLITE:
+#endif /* LWIP_UDPLITE */
+ /* Point to payload. */
+ pbuf_header(p, -(s16_t)ip_data.current_ip_header_tot_len);
+ udp_input(p, inp);
+ break;
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+ case IP6_NEXTH_TCP:
+ /* Point to payload. */
+ pbuf_header(p, -(s16_t)ip_data.current_ip_header_tot_len);
+ tcp_input(p, inp);
+ break;
+#endif /* LWIP_TCP */
+#if LWIP_ICMP6
+ case IP6_NEXTH_ICMP6:
+ /* Point to payload. */
+ pbuf_header(p, -(s16_t)ip_data.current_ip_header_tot_len);
+ icmp6_input(p, inp);
+ break;
+#endif /* LWIP_ICMP */
+ default:
+#if LWIP_ICMP6
+ /* send ICMP parameter problem unless it was a multicast or ICMPv6 */
+ if ((!ip6_addr_ismulticast(ip6_current_dest_addr())) &&
+ (IP6H_NEXTH(ip6hdr) != IP6_NEXTH_ICMP6)) {
+ icmp6_param_problem(p, ICMP6_PP_HEADER, ip_data.current_ip_header_tot_len - hlen);
+ }
+#endif /* LWIP_ICMP */
+ LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip6_input: Unsupported transport protocol %"U16_F"\n", (u16_t)IP6H_NEXTH(ip6hdr)));
+ pbuf_free(p);
+ IP6_STATS_INC(ip6.proterr);
+ IP6_STATS_INC(ip6.drop);
+ break;
+ }
+ }
+
+ip6_input_cleanup:
+ ip_data.current_netif = NULL;
+ ip_data.current_input_netif = NULL;
+ ip_data.current_ip6_header = NULL;
+ ip_data.current_ip_header_tot_len = 0;
+ ip6_addr_set_zero(ip6_current_src_addr());
+ ip6_addr_set_zero(ip6_current_dest_addr());
+
+ return ERR_OK;
+}
+
+
+/**
+ * Sends an IPv6 packet on a network interface. This function constructs
+ * the IPv6 header. If the source IPv6 address is NULL, the IPv6 "ANY" address is
+ * used as source (usually during network startup). If the source IPv6 address it
+ * IP6_ADDR_ANY, the most appropriate IPv6 address of the outgoing network
+ * interface is filled in as source address. If the destination IPv6 address is
+ * LWIP_IP_HDRINCL, p is assumed to already include an IPv6 header and
+ * p->payload points to it instead of the data.
+ *
+ * @param p the packet to send (p->payload points to the data, e.g. next
+ protocol header; if dest == LWIP_IP_HDRINCL, p already includes an
+ IPv6 header and p->payload points to that IPv6 header)
+ * @param src the source IPv6 address to send from (if src == IP6_ADDR_ANY, an
+ * IP address of the netif is selected and used as source address.
+ * if src == NULL, IP6_ADDR_ANY is used as source)
+ * @param dest the destination IPv6 address to send the packet to
+ * @param hl the Hop Limit value to be set in the IPv6 header
+ * @param tc the Traffic Class value to be set in the IPv6 header
+ * @param nexth the Next Header to be set in the IPv6 header
+ * @param netif the netif on which to send this packet
+ * @return ERR_OK if the packet was sent OK
+ * ERR_BUF if p doesn't have enough space for IPv6/LINK headers
+ * returns errors returned by netif->output
+ */
+err_t
+ip6_output_if(struct pbuf *p, const ip6_addr_t *src, const ip6_addr_t *dest,
+ u8_t hl, u8_t tc,
+ u8_t nexth, struct netif *netif)
+{
+ const ip6_addr_t *src_used = src;
+ if (dest != LWIP_IP_HDRINCL) {
+ if (src != NULL && ip6_addr_isany(src)) {
+ src_used = ip_2_ip6(ip6_select_source_address(netif, dest));
+ if ((src_used == NULL) || ip6_addr_isany(src_used)) {
+ /* No appropriate source address was found for this packet. */
+ LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip6_output: No suitable source address for packet.\n"));
+ IP6_STATS_INC(ip6.rterr);
+ return ERR_RTE;
+ }
+ }
+ }
+ return ip6_output_if_src(p, src_used, dest, hl, tc, nexth, netif);
+}
+
+/**
+ * Same as ip6_output_if() but 'src' address is not replaced by netif address
+ * when it is 'any'.
+ */
+err_t
+ip6_output_if_src(struct pbuf *p, const ip6_addr_t *src, const ip6_addr_t *dest,
+ u8_t hl, u8_t tc,
+ u8_t nexth, struct netif *netif)
+{
+ struct ip6_hdr *ip6hdr;
+ ip6_addr_t dest_addr;
+
+ LWIP_IP_CHECK_PBUF_REF_COUNT_FOR_TX(p);
+
+ /* Should the IPv6 header be generated or is it already included in p? */
+ if (dest != LWIP_IP_HDRINCL) {
+ /* generate IPv6 header */
+ if (pbuf_header(p, IP6_HLEN)) {
+ LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip6_output: not enough room for IPv6 header in pbuf\n"));
+ IP6_STATS_INC(ip6.err);
+ return ERR_BUF;
+ }
+
+ ip6hdr = (struct ip6_hdr *)p->payload;
+ LWIP_ASSERT("check that first pbuf can hold struct ip6_hdr",
+ (p->len >= sizeof(struct ip6_hdr)));
+
+ IP6H_HOPLIM_SET(ip6hdr, hl);
+ IP6H_NEXTH_SET(ip6hdr, nexth);
+
+ /* dest cannot be NULL here */
+ ip6_addr_copy(ip6hdr->dest, *dest);
+
+ IP6H_VTCFL_SET(ip6hdr, 6, tc, 0);
+ IP6H_PLEN_SET(ip6hdr, p->tot_len - IP6_HLEN);
+
+ if (src == NULL) {
+ src = IP6_ADDR_ANY6;
+ }
+ /* src cannot be NULL here */
+ ip6_addr_copy(ip6hdr->src, *src);
+
+ } else {
+ /* IP header already included in p */
+ ip6hdr = (struct ip6_hdr *)p->payload;
+ ip6_addr_copy(dest_addr, ip6hdr->dest);
+ dest = &dest_addr;
+ }
+
+ IP6_STATS_INC(ip6.xmit);
+
+ LWIP_DEBUGF(IP6_DEBUG, ("ip6_output_if: %c%c%"U16_F"\n", netif->name[0], netif->name[1], (u16_t)netif->num));
+ ip6_debug_print(p);
+
+#if ENABLE_LOOPBACK
+ {
+ int i;
+#if !LWIP_HAVE_LOOPIF
+ if (ip6_addr_isloopback(dest)) {
+ return netif_loop_output(netif, p);
+ }
+#endif /* !LWIP_HAVE_LOOPIF */
+ for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+ if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) &&
+ ip6_addr_cmp(dest, netif_ip6_addr(netif, i))) {
+ /* Packet to self, enqueue it for loopback */
+ LWIP_DEBUGF(IP6_DEBUG, ("netif_loop_output()\n"));
+ return netif_loop_output(netif, p);
+ }
+ }
+ }
+#endif /* ENABLE_LOOPBACK */
+#if LWIP_IPV6_FRAG
+ /* don't fragment if interface has mtu set to 0 [loopif] */
+ if (netif->mtu && (p->tot_len > nd6_get_destination_mtu(dest, netif))) {
+ return ip6_frag(p, netif, dest);
+ }
+#endif /* LWIP_IPV6_FRAG */
+
+ LWIP_DEBUGF(IP6_DEBUG, ("netif->output_ip6()\n"));
+ return netif->output_ip6(netif, p, dest);
+}
+
+/**
+ * Simple interface to ip6_output_if. It finds the outgoing network
+ * interface and calls upon ip6_output_if to do the actual work.
+ *
+ * @param p the packet to send (p->payload points to the data, e.g. next
+ protocol header; if dest == LWIP_IP_HDRINCL, p already includes an
+ IPv6 header and p->payload points to that IPv6 header)
+ * @param src the source IPv6 address to send from (if src == IP6_ADDR_ANY, an
+ * IP address of the netif is selected and used as source address.
+ * if src == NULL, IP6_ADDR_ANY is used as source)
+ * @param dest the destination IPv6 address to send the packet to
+ * @param hl the Hop Limit value to be set in the IPv6 header
+ * @param tc the Traffic Class value to be set in the IPv6 header
+ * @param nexth the Next Header to be set in the IPv6 header
+ *
+ * @return ERR_RTE if no route is found
+ * see ip_output_if() for more return values
+ */
+err_t
+ip6_output(struct pbuf *p, const ip6_addr_t *src, const ip6_addr_t *dest,
+ u8_t hl, u8_t tc, u8_t nexth)
+{
+ struct netif *netif;
+ struct ip6_hdr *ip6hdr;
+ ip6_addr_t src_addr, dest_addr;
+
+ LWIP_IP_CHECK_PBUF_REF_COUNT_FOR_TX(p);
+
+ if (dest != LWIP_IP_HDRINCL) {
+ netif = ip6_route(src, dest);
+ } else {
+ /* IP header included in p, read addresses. */
+ ip6hdr = (struct ip6_hdr *)p->payload;
+ ip6_addr_copy(src_addr, ip6hdr->src);
+ ip6_addr_copy(dest_addr, ip6hdr->dest);
+ netif = ip6_route(&src_addr, &dest_addr);
+ }
+
+ if (netif == NULL) {
+ LWIP_DEBUGF(IP6_DEBUG, ("ip6_output: no route for %"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F"\n",
+ IP6_ADDR_BLOCK1(dest),
+ IP6_ADDR_BLOCK2(dest),
+ IP6_ADDR_BLOCK3(dest),
+ IP6_ADDR_BLOCK4(dest),
+ IP6_ADDR_BLOCK5(dest),
+ IP6_ADDR_BLOCK6(dest),
+ IP6_ADDR_BLOCK7(dest),
+ IP6_ADDR_BLOCK8(dest)));
+ IP6_STATS_INC(ip6.rterr);
+ return ERR_RTE;
+ }
+
+ return ip6_output_if(p, src, dest, hl, tc, nexth, netif);
+}
+
+
+#if LWIP_NETIF_HWADDRHINT
+/** Like ip6_output, but takes and addr_hint pointer that is passed on to netif->addr_hint
+ * before calling ip6_output_if.
+ *
+ * @param p the packet to send (p->payload points to the data, e.g. next
+ protocol header; if dest == LWIP_IP_HDRINCL, p already includes an
+ IPv6 header and p->payload points to that IPv6 header)
+ * @param src the source IPv6 address to send from (if src == IP6_ADDR_ANY, an
+ * IP address of the netif is selected and used as source address.
+ * if src == NULL, IP6_ADDR_ANY is used as source)
+ * @param dest the destination IPv6 address to send the packet to
+ * @param hl the Hop Limit value to be set in the IPv6 header
+ * @param tc the Traffic Class value to be set in the IPv6 header
+ * @param nexth the Next Header to be set in the IPv6 header
+ * @param addr_hint address hint pointer set to netif->addr_hint before
+ * calling ip_output_if()
+ *
+ * @return ERR_RTE if no route is found
+ * see ip_output_if() for more return values
+ */
+err_t
+ip6_output_hinted(struct pbuf *p, const ip6_addr_t *src, const ip6_addr_t *dest,
+ u8_t hl, u8_t tc, u8_t nexth, u8_t *addr_hint)
+{
+ struct netif *netif;
+ struct ip6_hdr *ip6hdr;
+ ip6_addr_t src_addr, dest_addr;
+ err_t err;
+
+ LWIP_IP_CHECK_PBUF_REF_COUNT_FOR_TX(p);
+
+ if (dest != LWIP_IP_HDRINCL) {
+ netif = ip6_route(src, dest);
+ } else {
+ /* IP header included in p, read addresses. */
+ ip6hdr = (struct ip6_hdr *)p->payload;
+ ip6_addr_copy(src_addr, ip6hdr->src);
+ ip6_addr_copy(dest_addr, ip6hdr->dest);
+ netif = ip6_route(&src_addr, &dest_addr);
+ }
+
+ if (netif == NULL) {
+ LWIP_DEBUGF(IP6_DEBUG, ("ip6_output: no route for %"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F"\n",
+ IP6_ADDR_BLOCK1(dest),
+ IP6_ADDR_BLOCK2(dest),
+ IP6_ADDR_BLOCK3(dest),
+ IP6_ADDR_BLOCK4(dest),
+ IP6_ADDR_BLOCK5(dest),
+ IP6_ADDR_BLOCK6(dest),
+ IP6_ADDR_BLOCK7(dest),
+ IP6_ADDR_BLOCK8(dest)));
+ IP6_STATS_INC(ip6.rterr);
+ return ERR_RTE;
+ }
+
+ NETIF_SET_HWADDRHINT(netif, addr_hint);
+ err = ip6_output_if(p, src, dest, hl, tc, nexth, netif);
+ NETIF_SET_HWADDRHINT(netif, NULL);
+
+ return err;
+}
+#endif /* LWIP_NETIF_HWADDRHINT*/
+
+#if LWIP_IPV6_MLD
+/**
+ * Add a hop-by-hop options header with a router alert option and padding.
+ *
+ * Used by MLD when sending a Multicast listener report/done message.
+ *
+ * @param p the packet to which we will prepend the options header
+ * @param nexth the next header protocol number (e.g. IP6_NEXTH_ICMP6)
+ * @param value the value of the router alert option data (e.g. IP6_ROUTER_ALERT_VALUE_MLD)
+ * @return ERR_OK if hop-by-hop header was added, ERR_* otherwise
+ */
+err_t
+ip6_options_add_hbh_ra(struct pbuf *p, u8_t nexth, u8_t value)
+{
+ struct ip6_hbh_hdr *hbh_hdr;
+
+ /* Move pointer to make room for hop-by-hop options header. */
+ if (pbuf_header(p, sizeof(struct ip6_hbh_hdr))) {
+ LWIP_DEBUGF(IP6_DEBUG, ("ip6_options: no space for options header\n"));
+ IP6_STATS_INC(ip6.err);
+ return ERR_BUF;
+ }
+
+ hbh_hdr = (struct ip6_hbh_hdr *)p->payload;
+
+ /* Set fields. */
+ hbh_hdr->_nexth = nexth;
+ hbh_hdr->_hlen = 0;
+ hbh_hdr->_ra_opt_type = IP6_ROUTER_ALERT_OPTION;
+ hbh_hdr->_ra_opt_dlen = 2;
+ hbh_hdr->_ra_opt_data = value;
+ hbh_hdr->_padn_opt_type = IP6_PADN_ALERT_OPTION;
+ hbh_hdr->_padn_opt_dlen = 0;
+
+ return ERR_OK;
+}
+#endif /* LWIP_IPV6_MLD */
+
+#if IP6_DEBUG
+/* Print an IPv6 header by using LWIP_DEBUGF
+ * @param p an IPv6 packet, p->payload pointing to the IPv6 header
+ */
+void
+ip6_debug_print(struct pbuf *p)
+{
+ struct ip6_hdr *ip6hdr = (struct ip6_hdr *)p->payload;
+
+ LWIP_DEBUGF(IP6_DEBUG, ("IPv6 header:\n"));
+ LWIP_DEBUGF(IP6_DEBUG, ("+-------------------------------+\n"));
+ LWIP_DEBUGF(IP6_DEBUG, ("| %2"U16_F" | %3"U16_F" | %7"U32_F" | (ver, class, flow)\n",
+ IP6H_V(ip6hdr),
+ IP6H_TC(ip6hdr),
+ IP6H_FL(ip6hdr)));
+ LWIP_DEBUGF(IP6_DEBUG, ("+-------------------------------+\n"));
+ LWIP_DEBUGF(IP6_DEBUG, ("| %5"U16_F" | %3"U16_F" | %3"U16_F" | (plen, nexth, hopl)\n",
+ IP6H_PLEN(ip6hdr),
+ IP6H_NEXTH(ip6hdr),
+ IP6H_HOPLIM(ip6hdr)));
+ LWIP_DEBUGF(IP6_DEBUG, ("+-------------------------------+\n"));
+ LWIP_DEBUGF(IP6_DEBUG, ("| %4"X32_F" | %4"X32_F" | %4"X32_F" | %4"X32_F" | (src)\n",
+ IP6_ADDR_BLOCK1(&(ip6hdr->src)),
+ IP6_ADDR_BLOCK2(&(ip6hdr->src)),
+ IP6_ADDR_BLOCK3(&(ip6hdr->src)),
+ IP6_ADDR_BLOCK4(&(ip6hdr->src))));
+ LWIP_DEBUGF(IP6_DEBUG, ("| %4"X32_F" | %4"X32_F" | %4"X32_F" | %4"X32_F" |\n",
+ IP6_ADDR_BLOCK5(&(ip6hdr->src)),
+ IP6_ADDR_BLOCK6(&(ip6hdr->src)),
+ IP6_ADDR_BLOCK7(&(ip6hdr->src)),
+ IP6_ADDR_BLOCK8(&(ip6hdr->src))));
+ LWIP_DEBUGF(IP6_DEBUG, ("+-------------------------------+\n"));
+ LWIP_DEBUGF(IP6_DEBUG, ("| %4"X32_F" | %4"X32_F" | %4"X32_F" | %4"X32_F" | (dest)\n",
+ IP6_ADDR_BLOCK1(&(ip6hdr->dest)),
+ IP6_ADDR_BLOCK2(&(ip6hdr->dest)),
+ IP6_ADDR_BLOCK3(&(ip6hdr->dest)),
+ IP6_ADDR_BLOCK4(&(ip6hdr->dest))));
+ LWIP_DEBUGF(IP6_DEBUG, ("| %4"X32_F" | %4"X32_F" | %4"X32_F" | %4"X32_F" |\n",
+ IP6_ADDR_BLOCK5(&(ip6hdr->dest)),
+ IP6_ADDR_BLOCK6(&(ip6hdr->dest)),
+ IP6_ADDR_BLOCK7(&(ip6hdr->dest)),
+ IP6_ADDR_BLOCK8(&(ip6hdr->dest))));
+ LWIP_DEBUGF(IP6_DEBUG, ("+-------------------------------+\n"));
+}
+#endif /* IP6_DEBUG */
+
+#endif /* LWIP_IPV6 */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/ipv6/ip6_addr.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/ipv6/ip6_addr.c
new file mode 100644
index 0000000..aa06659
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/ipv6/ip6_addr.c
@@ -0,0 +1,292 @@
+/**
+ * @file
+ *
+ * IPv6 addresses.
+ */
+
+/*
+ * Copyright (c) 2010 Inico Technologies Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Ivan Delamer <delamer@inicotech.com>
+ *
+ * Functions for handling IPv6 addresses.
+ *
+ * Please coordinate changes and requests with Ivan Delamer
+ * <delamer@inicotech.com>
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/ip_addr.h"
+#include "lwip/def.h"
+
+/* used by IP6_ADDR_ANY(6) in ip6_addr.h */
+const ip_addr_t ip6_addr_any = IPADDR6_INIT(0ul, 0ul, 0ul, 0ul);
+
+#ifndef isprint
+#define in_range(c, lo, up) ((u8_t)c >= lo && (u8_t)c <= up)
+#define isprint(c) in_range(c, 0x20, 0x7f)
+#define isdigit(c) in_range(c, '0', '9')
+#define isxdigit(c) (isdigit(c) || in_range(c, 'a', 'f') || in_range(c, 'A', 'F'))
+#define islower(c) in_range(c, 'a', 'z')
+#define isspace(c) (c == ' ' || c == '\f' || c == '\n' || c == '\r' || c == '\t' || c == '\v')
+#define xchar(i) ((i) < 10 ? '0' + (i) : 'A' + (i) - 10)
+#endif
+
+/**
+ * Check whether "cp" is a valid ascii representation
+ * of an IPv6 address and convert to a binary address.
+ * Returns 1 if the address is valid, 0 if not.
+ *
+ * @param cp IPv6 address in ascii representation (e.g. "FF01::1")
+ * @param addr pointer to which to save the ip address in network order
+ * @return 1 if cp could be converted to addr, 0 on failure
+ */
+int
+ip6addr_aton(const char *cp, ip6_addr_t *addr)
+{
+ u32_t addr_index, zero_blocks, current_block_index, current_block_value;
+ const char *s;
+
+ /* Count the number of colons, to count the number of blocks in a "::" sequence
+ zero_blocks may be 1 even if there are no :: sequences */
+ zero_blocks = 8;
+ for (s = cp; *s != 0; s++) {
+ if (*s == ':') {
+ zero_blocks--;
+ } else if (!isxdigit(*s)) {
+ break;
+ }
+ }
+
+ /* parse each block */
+ addr_index = 0;
+ current_block_index = 0;
+ current_block_value = 0;
+ for (s = cp; *s != 0; s++) {
+ if (*s == ':') {
+ if (addr) {
+ if (current_block_index & 0x1) {
+ addr->addr[addr_index++] |= current_block_value;
+ }
+ else {
+ addr->addr[addr_index] = current_block_value << 16;
+ }
+ }
+ current_block_index++;
+ current_block_value = 0;
+ if (current_block_index > 7) {
+ /* address too long! */
+ return 0;
+ }
+ if (s[1] == ':') {
+ if (s[2] == ':') {
+ /* invalid format: three successive colons */
+ return 0;
+ }
+ s++;
+ /* "::" found, set zeros */
+ while (zero_blocks > 0) {
+ zero_blocks--;
+ if (current_block_index & 0x1) {
+ addr_index++;
+ } else {
+ if (addr) {
+ addr->addr[addr_index] = 0;
+ }
+ }
+ current_block_index++;
+ if (current_block_index > 7) {
+ /* address too long! */
+ return 0;
+ }
+ }
+ }
+ } else if (isxdigit(*s)) {
+ /* add current digit */
+ current_block_value = (current_block_value << 4) +
+ (isdigit(*s) ? (u32_t)(*s - '0') :
+ (u32_t)(10 + (islower(*s) ? *s - 'a' : *s - 'A')));
+ } else {
+ /* unexpected digit, space? CRLF? */
+ break;
+ }
+ }
+
+ if (addr) {
+ if (current_block_index & 0x1) {
+ addr->addr[addr_index++] |= current_block_value;
+ }
+ else {
+ addr->addr[addr_index] = current_block_value << 16;
+ }
+ }
+
+ /* convert to network byte order. */
+ if (addr) {
+ for (addr_index = 0; addr_index < 4; addr_index++) {
+ addr->addr[addr_index] = lwip_htonl(addr->addr[addr_index]);
+ }
+ }
+
+ if (current_block_index != 7) {
+ return 0;
+ }
+
+ return 1;
+}
+
+/**
+ * Convert numeric IPv6 address into ASCII representation.
+ * returns ptr to static buffer; not reentrant!
+ *
+ * @param addr ip6 address in network order to convert
+ * @return pointer to a global static (!) buffer that holds the ASCII
+ * representation of addr
+ */
+char *
+ip6addr_ntoa(const ip6_addr_t *addr)
+{
+ static char str[40];
+ return ip6addr_ntoa_r(addr, str, 40);
+}
+
+/**
+ * Same as ipaddr_ntoa, but reentrant since a user-supplied buffer is used.
+ *
+ * @param addr ip6 address in network order to convert
+ * @param buf target buffer where the string is stored
+ * @param buflen length of buf
+ * @return either pointer to buf which now holds the ASCII
+ * representation of addr or NULL if buf was too small
+ */
+char *
+ip6addr_ntoa_r(const ip6_addr_t *addr, char *buf, int buflen)
+{
+ u32_t current_block_index, current_block_value, next_block_value;
+ s32_t i;
+ u8_t zero_flag, empty_block_flag;
+
+ i = 0;
+ empty_block_flag = 0; /* used to indicate a zero chain for "::' */
+
+ for (current_block_index = 0; current_block_index < 8; current_block_index++) {
+ /* get the current 16-bit block */
+ current_block_value = lwip_htonl(addr->addr[current_block_index >> 1]);
+ if ((current_block_index & 0x1) == 0) {
+ current_block_value = current_block_value >> 16;
+ }
+ current_block_value &= 0xffff;
+
+ /* Check for empty block. */
+ if (current_block_value == 0) {
+ if (current_block_index == 7 && empty_block_flag == 1) {
+ /* special case, we must render a ':' for the last block. */
+ buf[i++] = ':';
+ if (i >= buflen) {
+ return NULL;
+ }
+ break;
+ }
+ if (empty_block_flag == 0) {
+ /* generate empty block "::", but only if more than one contiguous zero block,
+ * according to current formatting suggestions RFC 5952. */
+ next_block_value = lwip_htonl(addr->addr[(current_block_index + 1) >> 1]);
+ if ((current_block_index & 0x1) == 0x01) {
+ next_block_value = next_block_value >> 16;
+ }
+ next_block_value &= 0xffff;
+ if (next_block_value == 0) {
+ empty_block_flag = 1;
+ buf[i++] = ':';
+ if (i >= buflen) {
+ return NULL;
+ }
+ continue; /* move on to next block. */
+ }
+ } else if (empty_block_flag == 1) {
+ /* move on to next block. */
+ continue;
+ }
+ } else if (empty_block_flag == 1) {
+ /* Set this flag value so we don't produce multiple empty blocks. */
+ empty_block_flag = 2;
+ }
+
+ if (current_block_index > 0) {
+ buf[i++] = ':';
+ if (i >= buflen) {
+ return NULL;
+ }
+ }
+
+ if ((current_block_value & 0xf000) == 0) {
+ zero_flag = 1;
+ } else {
+ buf[i++] = xchar(((current_block_value & 0xf000) >> 12));
+ zero_flag = 0;
+ if (i >= buflen) {
+ return NULL;
+ }
+ }
+
+ if (((current_block_value & 0xf00) == 0) && (zero_flag)) {
+ /* do nothing */
+ } else {
+ buf[i++] = xchar(((current_block_value & 0xf00) >> 8));
+ zero_flag = 0;
+ if (i >= buflen) {
+ return NULL;
+ }
+ }
+
+ if (((current_block_value & 0xf0) == 0) && (zero_flag)) {
+ /* do nothing */
+ }
+ else {
+ buf[i++] = xchar(((current_block_value & 0xf0) >> 4));
+ zero_flag = 0;
+ if (i >= buflen) {
+ return NULL;
+ }
+ }
+
+ buf[i++] = xchar((current_block_value & 0xf));
+ if (i >= buflen) {
+ return NULL;
+ }
+ }
+
+ buf[i] = 0;
+
+ return buf;
+}
+
+#endif /* LWIP_IPV6 */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/ipv6/ip6_frag.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/ipv6/ip6_frag.c
new file mode 100644
index 0000000..ff07f71
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/ipv6/ip6_frag.c
@@ -0,0 +1,805 @@
+/**
+ * @file
+ *
+ * IPv6 fragmentation and reassembly.
+ */
+
+/*
+ * Copyright (c) 2010 Inico Technologies Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Ivan Delamer <delamer@inicotech.com>
+ *
+ *
+ * Please coordinate changes and requests with Ivan Delamer
+ * <delamer@inicotech.com>
+ */
+
+#include "lwip/opt.h"
+#include "lwip/ip6_frag.h"
+#include "lwip/ip6.h"
+#include "lwip/icmp6.h"
+#include "lwip/nd6.h"
+#include "lwip/ip.h"
+
+#include "lwip/pbuf.h"
+#include "lwip/memp.h"
+#include "lwip/stats.h"
+
+#include <string.h>
+
+#if LWIP_IPV6 && LWIP_IPV6_REASS /* don't build if not configured for use in lwipopts.h */
+
+
+/** Setting this to 0, you can turn off checking the fragments for overlapping
+ * regions. The code gets a little smaller. Only use this if you know that
+ * overlapping won't occur on your network! */
+#ifndef IP_REASS_CHECK_OVERLAP
+#define IP_REASS_CHECK_OVERLAP 1
+#endif /* IP_REASS_CHECK_OVERLAP */
+
+/** Set to 0 to prevent freeing the oldest datagram when the reassembly buffer is
+ * full (IP_REASS_MAX_PBUFS pbufs are enqueued). The code gets a little smaller.
+ * Datagrams will be freed by timeout only. Especially useful when MEMP_NUM_REASSDATA
+ * is set to 1, so one datagram can be reassembled at a time, only. */
+#ifndef IP_REASS_FREE_OLDEST
+#define IP_REASS_FREE_OLDEST 1
+#endif /* IP_REASS_FREE_OLDEST */
+
+#if IPV6_FRAG_COPYHEADER
+#define IPV6_FRAG_REQROOM ((s16_t)(sizeof(struct ip6_reass_helper) - IP6_FRAG_HLEN))
+#endif
+
+#define IP_REASS_FLAG_LASTFRAG 0x01
+
+/** This is a helper struct which holds the starting
+ * offset and the ending offset of this fragment to
+ * easily chain the fragments.
+ * It has the same packing requirements as the IPv6 header, since it replaces
+ * the Fragment Header in memory in incoming fragments to keep
+ * track of the various fragments.
+ */
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct ip6_reass_helper {
+ PACK_STRUCT_FIELD(struct pbuf *next_pbuf);
+ PACK_STRUCT_FIELD(u16_t start);
+ PACK_STRUCT_FIELD(u16_t end);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+/* static variables */
+static struct ip6_reassdata *reassdatagrams;
+static u16_t ip6_reass_pbufcount;
+
+/* Forward declarations. */
+static void ip6_reass_free_complete_datagram(struct ip6_reassdata *ipr);
+#if IP_REASS_FREE_OLDEST
+static void ip6_reass_remove_oldest_datagram(struct ip6_reassdata *ipr, int pbufs_needed);
+#endif /* IP_REASS_FREE_OLDEST */
+
+void
+ip6_reass_tmr(void)
+{
+ struct ip6_reassdata *r, *tmp;
+
+#if !IPV6_FRAG_COPYHEADER
+ LWIP_ASSERT("sizeof(struct ip6_reass_helper) <= IP6_FRAG_HLEN, set IPV6_FRAG_COPYHEADER to 1",
+ sizeof(struct ip6_reass_helper) <= IP6_FRAG_HLEN);
+#endif /* !IPV6_FRAG_COPYHEADER */
+
+ r = reassdatagrams;
+ while (r != NULL) {
+ /* Decrement the timer. Once it reaches 0,
+ * clean up the incomplete fragment assembly */
+ if (r->timer > 0) {
+ r->timer--;
+ r = r->next;
+ } else {
+ /* reassembly timed out */
+ tmp = r;
+ /* get the next pointer before freeing */
+ r = r->next;
+ /* free the helper struct and all enqueued pbufs */
+ ip6_reass_free_complete_datagram(tmp);
+ }
+ }
+}
+
+/**
+ * Free a datagram (struct ip6_reassdata) and all its pbufs.
+ * Updates the total count of enqueued pbufs (ip6_reass_pbufcount),
+ * sends an ICMP time exceeded packet.
+ *
+ * @param ipr datagram to free
+ */
+static void
+ip6_reass_free_complete_datagram(struct ip6_reassdata *ipr)
+{
+ struct ip6_reassdata *prev;
+ u16_t pbufs_freed = 0;
+ u16_t clen;
+ struct pbuf *p;
+ struct ip6_reass_helper *iprh;
+
+#if LWIP_ICMP6
+ iprh = (struct ip6_reass_helper *)ipr->p->payload;
+ if (iprh->start == 0) {
+ /* The first fragment was received, send ICMP time exceeded. */
+ /* First, de-queue the first pbuf from r->p. */
+ p = ipr->p;
+ ipr->p = iprh->next_pbuf;
+ /* Then, move back to the original ipv6 header (we are now pointing to Fragment header).
+ This cannot fail since we already checked when receiving this fragment. */
+ if (pbuf_header_force(p, (s16_t)((u8_t*)p->payload - (u8_t*)IPV6_FRAG_HDRREF(ipr->iphdr)))) {
+ LWIP_ASSERT("ip6_reass_free: moving p->payload to ip6 header failed\n", 0);
+ }
+ else {
+ icmp6_time_exceeded(p, ICMP6_TE_FRAG);
+ }
+ clen = pbuf_clen(p);
+ LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff);
+ pbufs_freed += clen;
+ pbuf_free(p);
+ }
+#endif /* LWIP_ICMP6 */
+
+ /* First, free all received pbufs. The individual pbufs need to be released
+ separately as they have not yet been chained */
+ p = ipr->p;
+ while (p != NULL) {
+ struct pbuf *pcur;
+ iprh = (struct ip6_reass_helper *)p->payload;
+ pcur = p;
+ /* get the next pointer before freeing */
+ p = iprh->next_pbuf;
+ clen = pbuf_clen(pcur);
+ LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff);
+ pbufs_freed += clen;
+ pbuf_free(pcur);
+ }
+
+ /* Then, unchain the struct ip6_reassdata from the list and free it. */
+ if (ipr == reassdatagrams) {
+ reassdatagrams = ipr->next;
+ } else {
+ prev = reassdatagrams;
+ while (prev != NULL) {
+ if (prev->next == ipr) {
+ break;
+ }
+ prev = prev->next;
+ }
+ if (prev != NULL) {
+ prev->next = ipr->next;
+ }
+ }
+ memp_free(MEMP_IP6_REASSDATA, ipr);
+
+ /* Finally, update number of pbufs in reassembly queue */
+ LWIP_ASSERT("ip_reass_pbufcount >= clen", ip6_reass_pbufcount >= pbufs_freed);
+ ip6_reass_pbufcount -= pbufs_freed;
+}
+
+#if IP_REASS_FREE_OLDEST
+/**
+ * Free the oldest datagram to make room for enqueueing new fragments.
+ * The datagram ipr is not freed!
+ *
+ * @param ipr ip6_reassdata for the current fragment
+ * @param pbufs_needed number of pbufs needed to enqueue
+ * (used for freeing other datagrams if not enough space)
+ */
+static void
+ip6_reass_remove_oldest_datagram(struct ip6_reassdata *ipr, int pbufs_needed)
+{
+ struct ip6_reassdata *r, *oldest;
+
+ /* Free datagrams until being allowed to enqueue 'pbufs_needed' pbufs,
+ * but don't free the current datagram! */
+ do {
+ r = oldest = reassdatagrams;
+ while (r != NULL) {
+ if (r != ipr) {
+ if (r->timer <= oldest->timer) {
+ /* older than the previous oldest */
+ oldest = r;
+ }
+ }
+ r = r->next;
+ }
+ if (oldest == ipr) {
+ /* nothing to free, ipr is the only element on the list */
+ return;
+ }
+ if (oldest != NULL) {
+ ip6_reass_free_complete_datagram(oldest);
+ }
+ } while (((ip6_reass_pbufcount + pbufs_needed) > IP_REASS_MAX_PBUFS) && (reassdatagrams != NULL));
+}
+#endif /* IP_REASS_FREE_OLDEST */
+
+/**
+ * Reassembles incoming IPv6 fragments into an IPv6 datagram.
+ *
+ * @param p points to the IPv6 Fragment Header
+ * @return NULL if reassembly is incomplete, pbuf pointing to
+ * IPv6 Header if reassembly is complete
+ */
+struct pbuf *
+ip6_reass(struct pbuf *p)
+{
+ struct ip6_reassdata *ipr, *ipr_prev;
+ struct ip6_reass_helper *iprh, *iprh_tmp, *iprh_prev=NULL;
+ struct ip6_frag_hdr *frag_hdr;
+ u16_t offset, len;
+ u16_t clen;
+ u8_t valid = 1;
+ struct pbuf *q;
+
+ IP6_FRAG_STATS_INC(ip6_frag.recv);
+
+ if ((const void*)ip6_current_header() != ((u8_t*)p->payload) - IP6_HLEN) {
+ /* ip6_frag_hdr must be in the first pbuf, not chained */
+ IP6_FRAG_STATS_INC(ip6_frag.proterr);
+ IP6_FRAG_STATS_INC(ip6_frag.drop);
+ goto nullreturn;
+ }
+
+ frag_hdr = (struct ip6_frag_hdr *) p->payload;
+
+ clen = pbuf_clen(p);
+
+ offset = lwip_ntohs(frag_hdr->_fragment_offset);
+
+ /* Calculate fragment length from IPv6 payload length.
+ * Adjust for headers before Fragment Header.
+ * And finally adjust by Fragment Header length. */
+ len = lwip_ntohs(ip6_current_header()->_plen);
+ len -= (u16_t)(((u8_t*)p->payload - (const u8_t*)ip6_current_header()) - IP6_HLEN);
+ len -= IP6_FRAG_HLEN;
+
+ /* Look for the datagram the fragment belongs to in the current datagram queue,
+ * remembering the previous in the queue for later dequeueing. */
+ for (ipr = reassdatagrams, ipr_prev = NULL; ipr != NULL; ipr = ipr->next) {
+ /* Check if the incoming fragment matches the one currently present
+ in the reassembly buffer. If so, we proceed with copying the
+ fragment into the buffer. */
+ if ((frag_hdr->_identification == ipr->identification) &&
+ ip6_addr_cmp(ip6_current_src_addr(), &(IPV6_FRAG_HDRREF(ipr->iphdr)->src)) &&
+ ip6_addr_cmp(ip6_current_dest_addr(), &(IPV6_FRAG_HDRREF(ipr->iphdr)->dest))) {
+ IP6_FRAG_STATS_INC(ip6_frag.cachehit);
+ break;
+ }
+ ipr_prev = ipr;
+ }
+
+ if (ipr == NULL) {
+ /* Enqueue a new datagram into the datagram queue */
+ ipr = (struct ip6_reassdata *)memp_malloc(MEMP_IP6_REASSDATA);
+ if (ipr == NULL) {
+#if IP_REASS_FREE_OLDEST
+ /* Make room and try again. */
+ ip6_reass_remove_oldest_datagram(ipr, clen);
+ ipr = (struct ip6_reassdata *)memp_malloc(MEMP_IP6_REASSDATA);
+ if (ipr != NULL) {
+ /* re-search ipr_prev since it might have been removed */
+ for (ipr_prev = reassdatagrams; ipr_prev != NULL; ipr_prev = ipr_prev->next) {
+ if (ipr_prev->next == ipr) {
+ break;
+ }
+ }
+ } else
+#endif /* IP_REASS_FREE_OLDEST */
+ {
+ IP6_FRAG_STATS_INC(ip6_frag.memerr);
+ IP6_FRAG_STATS_INC(ip6_frag.drop);
+ goto nullreturn;
+ }
+ }
+
+ memset(ipr, 0, sizeof(struct ip6_reassdata));
+ ipr->timer = IP_REASS_MAXAGE;
+
+ /* enqueue the new structure to the front of the list */
+ ipr->next = reassdatagrams;
+ reassdatagrams = ipr;
+
+ /* Use the current IPv6 header for src/dest address reference.
+ * Eventually, we will replace it when we get the first fragment
+ * (it might be this one, in any case, it is done later). */
+#if IPV6_FRAG_COPYHEADER
+ MEMCPY(&ipr->iphdr, ip6_current_header(), IP6_HLEN);
+#else /* IPV6_FRAG_COPYHEADER */
+ /* need to use the none-const pointer here: */
+ ipr->iphdr = ip_data.current_ip6_header;
+#endif /* IPV6_FRAG_COPYHEADER */
+
+ /* copy the fragmented packet id. */
+ ipr->identification = frag_hdr->_identification;
+
+ /* copy the nexth field */
+ ipr->nexth = frag_hdr->_nexth;
+ }
+
+ /* Check if we are allowed to enqueue more datagrams. */
+ if ((ip6_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS) {
+#if IP_REASS_FREE_OLDEST
+ ip6_reass_remove_oldest_datagram(ipr, clen);
+ if ((ip6_reass_pbufcount + clen) <= IP_REASS_MAX_PBUFS) {
+ /* re-search ipr_prev since it might have been removed */
+ for (ipr_prev = reassdatagrams; ipr_prev != NULL; ipr_prev = ipr_prev->next) {
+ if (ipr_prev->next == ipr) {
+ break;
+ }
+ }
+ } else
+#endif /* IP_REASS_FREE_OLDEST */
+ {
+ /* @todo: send ICMPv6 time exceeded here? */
+ /* drop this pbuf */
+ IP6_FRAG_STATS_INC(ip6_frag.memerr);
+ IP6_FRAG_STATS_INC(ip6_frag.drop);
+ goto nullreturn;
+ }
+ }
+
+ /* Overwrite Fragment Header with our own helper struct. */
+#if IPV6_FRAG_COPYHEADER
+ if (IPV6_FRAG_REQROOM > 0) {
+ /* Make room for struct ip6_reass_helper (only required if sizeof(void*) > 4).
+ This cannot fail since we already checked when receiving this fragment. */
+ u8_t hdrerr = pbuf_header_force(p, IPV6_FRAG_REQROOM);
+ LWIP_UNUSED_ARG(hdrerr); /* in case of LWIP_NOASSERT */
+ LWIP_ASSERT("no room for struct ip6_reass_helper", hdrerr == 0);
+ }
+#else /* IPV6_FRAG_COPYHEADER */
+ LWIP_ASSERT("sizeof(struct ip6_reass_helper) <= IP6_FRAG_HLEN, set IPV6_FRAG_COPYHEADER to 1",
+ sizeof(struct ip6_reass_helper) <= IP6_FRAG_HLEN);
+#endif /* IPV6_FRAG_COPYHEADER */
+ iprh = (struct ip6_reass_helper *)p->payload;
+ iprh->next_pbuf = NULL;
+ iprh->start = (offset & IP6_FRAG_OFFSET_MASK);
+ iprh->end = (offset & IP6_FRAG_OFFSET_MASK) + len;
+
+ /* find the right place to insert this pbuf */
+ /* Iterate through until we either get to the end of the list (append),
+ * or we find on with a larger offset (insert). */
+ for (q = ipr->p; q != NULL;) {
+ iprh_tmp = (struct ip6_reass_helper*)q->payload;
+ if (iprh->start < iprh_tmp->start) {
+#if IP_REASS_CHECK_OVERLAP
+ if (iprh->end > iprh_tmp->start) {
+ /* fragment overlaps with following, throw away */
+ IP6_FRAG_STATS_INC(ip6_frag.proterr);
+ IP6_FRAG_STATS_INC(ip6_frag.drop);
+ goto nullreturn;
+ }
+ if (iprh_prev != NULL) {
+ if (iprh->start < iprh_prev->end) {
+ /* fragment overlaps with previous, throw away */
+ IP6_FRAG_STATS_INC(ip6_frag.proterr);
+ IP6_FRAG_STATS_INC(ip6_frag.drop);
+ goto nullreturn;
+ }
+ }
+#endif /* IP_REASS_CHECK_OVERLAP */
+ /* the new pbuf should be inserted before this */
+ iprh->next_pbuf = q;
+ if (iprh_prev != NULL) {
+ /* not the fragment with the lowest offset */
+ iprh_prev->next_pbuf = p;
+ } else {
+ /* fragment with the lowest offset */
+ ipr->p = p;
+ }
+ break;
+ } else if (iprh->start == iprh_tmp->start) {
+ /* received the same datagram twice: no need to keep the datagram */
+ IP6_FRAG_STATS_INC(ip6_frag.drop);
+ goto nullreturn;
+#if IP_REASS_CHECK_OVERLAP
+ } else if (iprh->start < iprh_tmp->end) {
+ /* overlap: no need to keep the new datagram */
+ IP6_FRAG_STATS_INC(ip6_frag.proterr);
+ IP6_FRAG_STATS_INC(ip6_frag.drop);
+ goto nullreturn;
+#endif /* IP_REASS_CHECK_OVERLAP */
+ } else {
+ /* Check if the fragments received so far have no gaps. */
+ if (iprh_prev != NULL) {
+ if (iprh_prev->end != iprh_tmp->start) {
+ /* There is a fragment missing between the current
+ * and the previous fragment */
+ valid = 0;
+ }
+ }
+ }
+ q = iprh_tmp->next_pbuf;
+ iprh_prev = iprh_tmp;
+ }
+
+ /* If q is NULL, then we made it to the end of the list. Determine what to do now */
+ if (q == NULL) {
+ if (iprh_prev != NULL) {
+ /* this is (for now), the fragment with the highest offset:
+ * chain it to the last fragment */
+#if IP_REASS_CHECK_OVERLAP
+ LWIP_ASSERT("check fragments don't overlap", iprh_prev->end <= iprh->start);
+#endif /* IP_REASS_CHECK_OVERLAP */
+ iprh_prev->next_pbuf = p;
+ if (iprh_prev->end != iprh->start) {
+ valid = 0;
+ }
+ } else {
+#if IP_REASS_CHECK_OVERLAP
+ LWIP_ASSERT("no previous fragment, this must be the first fragment!",
+ ipr->p == NULL);
+#endif /* IP_REASS_CHECK_OVERLAP */
+ /* this is the first fragment we ever received for this ip datagram */
+ ipr->p = p;
+ }
+ }
+
+ /* Track the current number of pbufs current 'in-flight', in order to limit
+ the number of fragments that may be enqueued at any one time */
+ ip6_reass_pbufcount += clen;
+
+ /* Remember IPv6 header if this is the first fragment. */
+ if (iprh->start == 0) {
+#if IPV6_FRAG_COPYHEADER
+ if (iprh->next_pbuf != NULL) {
+ MEMCPY(&ipr->iphdr, ip6_current_header(), IP6_HLEN);
+ }
+#else /* IPV6_FRAG_COPYHEADER */
+ /* need to use the none-const pointer here: */
+ ipr->iphdr = ip_data.current_ip6_header;
+#endif /* IPV6_FRAG_COPYHEADER */
+ }
+
+ /* If this is the last fragment, calculate total packet length. */
+ if ((offset & IP6_FRAG_MORE_FLAG) == 0) {
+ ipr->datagram_len = iprh->end;
+ }
+
+ /* Additional validity tests: we have received first and last fragment. */
+ iprh_tmp = (struct ip6_reass_helper*)ipr->p->payload;
+ if (iprh_tmp->start != 0) {
+ valid = 0;
+ }
+ if (ipr->datagram_len == 0) {
+ valid = 0;
+ }
+
+ /* Final validity test: no gaps between current and last fragment. */
+ iprh_prev = iprh;
+ q = iprh->next_pbuf;
+ while ((q != NULL) && valid) {
+ iprh = (struct ip6_reass_helper*)q->payload;
+ if (iprh_prev->end != iprh->start) {
+ valid = 0;
+ break;
+ }
+ iprh_prev = iprh;
+ q = iprh->next_pbuf;
+ }
+
+ if (valid) {
+ /* All fragments have been received */
+ struct ip6_hdr* iphdr_ptr;
+
+ /* chain together the pbufs contained within the ip6_reassdata list. */
+ iprh = (struct ip6_reass_helper*) ipr->p->payload;
+ while (iprh != NULL) {
+ struct pbuf* next_pbuf = iprh->next_pbuf;
+ if (next_pbuf != NULL) {
+ /* Save next helper struct (will be hidden in next step). */
+ iprh_tmp = (struct ip6_reass_helper*)next_pbuf->payload;
+
+ /* hide the fragment header for every succeeding fragment */
+ pbuf_header(next_pbuf, -IP6_FRAG_HLEN);
+#if IPV6_FRAG_COPYHEADER
+ if (IPV6_FRAG_REQROOM > 0) {
+ /* hide the extra bytes borrowed from ip6_hdr for struct ip6_reass_helper */
+ u8_t hdrerr = pbuf_header(next_pbuf, -(s16_t)(IPV6_FRAG_REQROOM));
+ LWIP_UNUSED_ARG(hdrerr); /* in case of LWIP_NOASSERT */
+ LWIP_ASSERT("no room for struct ip6_reass_helper", hdrerr == 0);
+ }
+#endif
+ pbuf_cat(ipr->p, next_pbuf);
+ }
+ else {
+ iprh_tmp = NULL;
+ }
+
+ iprh = iprh_tmp;
+ }
+
+#if IPV6_FRAG_COPYHEADER
+ if (IPV6_FRAG_REQROOM > 0) {
+ /* get back room for struct ip6_reass_helper (only required if sizeof(void*) > 4) */
+ u8_t hdrerr = pbuf_header(ipr->p, -(s16_t)(IPV6_FRAG_REQROOM));
+ LWIP_UNUSED_ARG(hdrerr); /* in case of LWIP_NOASSERT */
+ LWIP_ASSERT("no room for struct ip6_reass_helper", hdrerr == 0);
+ }
+ iphdr_ptr = (struct ip6_hdr*)((u8_t*)ipr->p->payload - IP6_HLEN);
+ MEMCPY(iphdr_ptr, &ipr->iphdr, IP6_HLEN);
+#else
+ iphdr_ptr = ipr->iphdr;
+#endif
+
+ /* Adjust datagram length by adding header lengths. */
+ ipr->datagram_len += (u16_t)(((u8_t*)ipr->p->payload - (u8_t*)iphdr_ptr)
+ + IP6_FRAG_HLEN
+ - IP6_HLEN);
+
+ /* Set payload length in ip header. */
+ iphdr_ptr->_plen = lwip_htons(ipr->datagram_len);
+
+ /* Get the first pbuf. */
+ p = ipr->p;
+
+ /* Restore Fragment Header in first pbuf. Mark as "single fragment"
+ * packet. Restore nexth. */
+ frag_hdr = (struct ip6_frag_hdr *) p->payload;
+ frag_hdr->_nexth = ipr->nexth;
+ frag_hdr->reserved = 0;
+ frag_hdr->_fragment_offset = 0;
+ frag_hdr->_identification = 0;
+
+ /* release the sources allocate for the fragment queue entry */
+ if (reassdatagrams == ipr) {
+ /* it was the first in the list */
+ reassdatagrams = ipr->next;
+ } else {
+ /* it wasn't the first, so it must have a valid 'prev' */
+ LWIP_ASSERT("sanity check linked list", ipr_prev != NULL);
+ ipr_prev->next = ipr->next;
+ }
+ memp_free(MEMP_IP6_REASSDATA, ipr);
+
+ /* adjust the number of pbufs currently queued for reassembly. */
+ ip6_reass_pbufcount -= pbuf_clen(p);
+
+ /* Move pbuf back to IPv6 header.
+ This cannot fail since we already checked when receiving this fragment. */
+ if (pbuf_header_force(p, (s16_t)((u8_t*)p->payload - (u8_t*)iphdr_ptr))) {
+ LWIP_ASSERT("ip6_reass: moving p->payload to ip6 header failed\n", 0);
+ pbuf_free(p);
+ return NULL;
+ }
+
+ /* Return the pbuf chain */
+ return p;
+ }
+ /* the datagram is not (yet?) reassembled completely */
+ return NULL;
+
+nullreturn:
+ pbuf_free(p);
+ return NULL;
+}
+
+#endif /* LWIP_IPV6 && LWIP_IPV6_REASS */
+
+#if LWIP_IPV6 && LWIP_IPV6_FRAG
+
+#if !LWIP_NETIF_TX_SINGLE_PBUF
+/** Allocate a new struct pbuf_custom_ref */
+static struct pbuf_custom_ref*
+ip6_frag_alloc_pbuf_custom_ref(void)
+{
+ return (struct pbuf_custom_ref*)memp_malloc(MEMP_FRAG_PBUF);
+}
+
+/** Free a struct pbuf_custom_ref */
+static void
+ip6_frag_free_pbuf_custom_ref(struct pbuf_custom_ref* p)
+{
+ LWIP_ASSERT("p != NULL", p != NULL);
+ memp_free(MEMP_FRAG_PBUF, p);
+}
+
+/** Free-callback function to free a 'struct pbuf_custom_ref', called by
+ * pbuf_free. */
+static void
+ip6_frag_free_pbuf_custom(struct pbuf *p)
+{
+ struct pbuf_custom_ref *pcr = (struct pbuf_custom_ref*)p;
+ LWIP_ASSERT("pcr != NULL", pcr != NULL);
+ LWIP_ASSERT("pcr == p", (void*)pcr == (void*)p);
+ if (pcr->original != NULL) {
+ pbuf_free(pcr->original);
+ }
+ ip6_frag_free_pbuf_custom_ref(pcr);
+}
+#endif /* !LWIP_NETIF_TX_SINGLE_PBUF */
+
+/**
+ * Fragment an IPv6 datagram if too large for the netif or path MTU.
+ *
+ * Chop the datagram in MTU sized chunks and send them in order
+ * by pointing PBUF_REFs into p
+ *
+ * @param p ipv6 packet to send
+ * @param netif the netif on which to send
+ * @param dest destination ipv6 address to which to send
+ *
+ * @return ERR_OK if sent successfully, err_t otherwise
+ */
+err_t
+ip6_frag(struct pbuf *p, struct netif *netif, const ip6_addr_t *dest)
+{
+ struct ip6_hdr *original_ip6hdr;
+ struct ip6_hdr *ip6hdr;
+ struct ip6_frag_hdr *frag_hdr;
+ struct pbuf *rambuf;
+#if !LWIP_NETIF_TX_SINGLE_PBUF
+ struct pbuf *newpbuf;
+ u16_t newpbuflen = 0;
+ u16_t left_to_copy;
+#endif
+ static u32_t identification;
+ u16_t nfb;
+ u16_t left, cop;
+ u16_t mtu;
+ u16_t fragment_offset = 0;
+ u16_t last;
+ u16_t poff = IP6_HLEN;
+
+ identification++;
+
+ original_ip6hdr = (struct ip6_hdr *)p->payload;
+
+ mtu = nd6_get_destination_mtu(dest, netif);
+
+ /* @todo we assume there are no options in the unfragmentable part (IPv6 header). */
+ left = p->tot_len - IP6_HLEN;
+
+ nfb = (mtu - (IP6_HLEN + IP6_FRAG_HLEN)) & IP6_FRAG_OFFSET_MASK;
+
+ while (left) {
+ last = (left <= nfb);
+
+ /* Fill this fragment */
+ cop = last ? left : nfb;
+
+#if LWIP_NETIF_TX_SINGLE_PBUF
+ rambuf = pbuf_alloc(PBUF_IP, cop + IP6_FRAG_HLEN, PBUF_RAM);
+ if (rambuf == NULL) {
+ IP6_FRAG_STATS_INC(ip6_frag.memerr);
+ return ERR_MEM;
+ }
+ LWIP_ASSERT("this needs a pbuf in one piece!",
+ (rambuf->len == rambuf->tot_len) && (rambuf->next == NULL));
+ poff += pbuf_copy_partial(p, (u8_t*)rambuf->payload + IP6_FRAG_HLEN, cop, poff);
+ /* make room for the IP header */
+ if (pbuf_header(rambuf, IP6_HLEN)) {
+ pbuf_free(rambuf);
+ IP6_FRAG_STATS_INC(ip6_frag.memerr);
+ return ERR_MEM;
+ }
+ /* fill in the IP header */
+ SMEMCPY(rambuf->payload, original_ip6hdr, IP6_HLEN);
+ ip6hdr = (struct ip6_hdr *)rambuf->payload;
+ frag_hdr = (struct ip6_frag_hdr *)((u8_t*)rambuf->payload + IP6_HLEN);
+#else
+ /* When not using a static buffer, create a chain of pbufs.
+ * The first will be a PBUF_RAM holding the link, IPv6, and Fragment header.
+ * The rest will be PBUF_REFs mirroring the pbuf chain to be fragged,
+ * but limited to the size of an mtu.
+ */
+ rambuf = pbuf_alloc(PBUF_LINK, IP6_HLEN + IP6_FRAG_HLEN, PBUF_RAM);
+ if (rambuf == NULL) {
+ IP6_FRAG_STATS_INC(ip6_frag.memerr);
+ return ERR_MEM;
+ }
+ LWIP_ASSERT("this needs a pbuf in one piece!",
+ (p->len >= (IP6_HLEN)));
+ SMEMCPY(rambuf->payload, original_ip6hdr, IP6_HLEN);
+ ip6hdr = (struct ip6_hdr *)rambuf->payload;
+ frag_hdr = (struct ip6_frag_hdr *)((u8_t*)rambuf->payload + IP6_HLEN);
+
+ /* Can just adjust p directly for needed offset. */
+ p->payload = (u8_t *)p->payload + poff;
+ p->len -= poff;
+ p->tot_len -= poff;
+
+ left_to_copy = cop;
+ while (left_to_copy) {
+ struct pbuf_custom_ref *pcr;
+ newpbuflen = (left_to_copy < p->len) ? left_to_copy : p->len;
+ /* Is this pbuf already empty? */
+ if (!newpbuflen) {
+ p = p->next;
+ continue;
+ }
+ pcr = ip6_frag_alloc_pbuf_custom_ref();
+ if (pcr == NULL) {
+ pbuf_free(rambuf);
+ IP6_FRAG_STATS_INC(ip6_frag.memerr);
+ return ERR_MEM;
+ }
+ /* Mirror this pbuf, although we might not need all of it. */
+ newpbuf = pbuf_alloced_custom(PBUF_RAW, newpbuflen, PBUF_REF, &pcr->pc, p->payload, newpbuflen);
+ if (newpbuf == NULL) {
+ ip6_frag_free_pbuf_custom_ref(pcr);
+ pbuf_free(rambuf);
+ IP6_FRAG_STATS_INC(ip6_frag.memerr);
+ return ERR_MEM;
+ }
+ pbuf_ref(p);
+ pcr->original = p;
+ pcr->pc.custom_free_function = ip6_frag_free_pbuf_custom;
+
+ /* Add it to end of rambuf's chain, but using pbuf_cat, not pbuf_chain
+ * so that it is removed when pbuf_dechain is later called on rambuf.
+ */
+ pbuf_cat(rambuf, newpbuf);
+ left_to_copy -= newpbuflen;
+ if (left_to_copy) {
+ p = p->next;
+ }
+ }
+ poff = newpbuflen;
+#endif /* LWIP_NETIF_TX_SINGLE_PBUF */
+
+ /* Set headers */
+ frag_hdr->_nexth = original_ip6hdr->_nexth;
+ frag_hdr->reserved = 0;
+ frag_hdr->_fragment_offset = lwip_htons((fragment_offset & IP6_FRAG_OFFSET_MASK) | (last ? 0 : IP6_FRAG_MORE_FLAG));
+ frag_hdr->_identification = lwip_htonl(identification);
+
+ IP6H_NEXTH_SET(ip6hdr, IP6_NEXTH_FRAGMENT);
+ IP6H_PLEN_SET(ip6hdr, cop + IP6_FRAG_HLEN);
+
+ /* No need for separate header pbuf - we allowed room for it in rambuf
+ * when allocated.
+ */
+ IP6_FRAG_STATS_INC(ip6_frag.xmit);
+ netif->output_ip6(netif, rambuf, dest);
+
+ /* Unfortunately we can't reuse rambuf - the hardware may still be
+ * using the buffer. Instead we free it (and the ensuing chain) and
+ * recreate it next time round the loop. If we're lucky the hardware
+ * will have already sent the packet, the free will really free, and
+ * there will be zero memory penalty.
+ */
+
+ pbuf_free(rambuf);
+ left -= cop;
+ fragment_offset += cop;
+ }
+ return ERR_OK;
+}
+
+#endif /* LWIP_IPV6 && LWIP_IPV6_FRAG */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/ipv6/mld6.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/ipv6/mld6.c
new file mode 100644
index 0000000..9acb82f
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/ipv6/mld6.c
@@ -0,0 +1,588 @@
+/**
+ * @file
+ * Multicast listener discovery
+ *
+ * @defgroup mld6 MLD6
+ * @ingroup ip6
+ * Multicast listener discovery for IPv6. Aims to be compliant with RFC 2710.
+ * No support for MLDv2.\n
+ * To be called from TCPIP thread
+ */
+
+/*
+ * Copyright (c) 2010 Inico Technologies Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Ivan Delamer <delamer@inicotech.com>
+ *
+ *
+ * Please coordinate changes and requests with Ivan Delamer
+ * <delamer@inicotech.com>
+ */
+
+/* Based on igmp.c implementation of igmp v2 protocol */
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV6 && LWIP_IPV6_MLD /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/mld6.h"
+#include "lwip/prot/mld6.h"
+#include "lwip/icmp6.h"
+#include "lwip/ip6.h"
+#include "lwip/ip6_addr.h"
+#include "lwip/ip.h"
+#include "lwip/inet_chksum.h"
+#include "lwip/pbuf.h"
+#include "lwip/netif.h"
+#include "lwip/memp.h"
+#include "lwip/stats.h"
+
+#include <string.h>
+
+
+/*
+ * MLD constants
+ */
+#define MLD6_HL 1
+#define MLD6_JOIN_DELAYING_MEMBER_TMR_MS (500)
+
+#define MLD6_GROUP_NON_MEMBER 0
+#define MLD6_GROUP_DELAYING_MEMBER 1
+#define MLD6_GROUP_IDLE_MEMBER 2
+
+/* Forward declarations. */
+static struct mld_group *mld6_new_group(struct netif *ifp, const ip6_addr_t *addr);
+static err_t mld6_remove_group(struct netif *netif, struct mld_group *group);
+static void mld6_delayed_report(struct mld_group *group, u16_t maxresp);
+static void mld6_send(struct netif *netif, struct mld_group *group, u8_t type);
+
+
+/**
+ * Stop MLD processing on interface
+ *
+ * @param netif network interface on which stop MLD processing
+ */
+err_t
+mld6_stop(struct netif *netif)
+{
+ struct mld_group *group = netif_mld6_data(netif);
+
+ netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_MLD6, NULL);
+
+ while (group != NULL) {
+ struct mld_group *next = group->next; /* avoid use-after-free below */
+
+ /* disable the group at the MAC level */
+ if (netif->mld_mac_filter != NULL) {
+ netif->mld_mac_filter(netif, &(group->group_address), NETIF_DEL_MAC_FILTER);
+ }
+
+ /* free group */
+ memp_free(MEMP_MLD6_GROUP, group);
+
+ /* move to "next" */
+ group = next;
+ }
+ return ERR_OK;
+}
+
+/**
+ * Report MLD memberships for this interface
+ *
+ * @param netif network interface on which report MLD memberships
+ */
+void
+mld6_report_groups(struct netif *netif)
+{
+ struct mld_group *group = netif_mld6_data(netif);
+
+ while (group != NULL) {
+ mld6_delayed_report(group, MLD6_JOIN_DELAYING_MEMBER_TMR_MS);
+ group = group->next;
+ }
+}
+
+/**
+ * Search for a group that is joined on a netif
+ *
+ * @param ifp the network interface for which to look
+ * @param addr the group ipv6 address to search for
+ * @return a struct mld_group* if the group has been found,
+ * NULL if the group wasn't found.
+ */
+struct mld_group *
+mld6_lookfor_group(struct netif *ifp, const ip6_addr_t *addr)
+{
+ struct mld_group *group = netif_mld6_data(ifp);
+
+ while (group != NULL) {
+ if (ip6_addr_cmp(&(group->group_address), addr)) {
+ return group;
+ }
+ group = group->next;
+ }
+
+ return NULL;
+}
+
+
+/**
+ * create a new group
+ *
+ * @param ifp the network interface for which to create
+ * @param addr the new group ipv6
+ * @return a struct mld_group*,
+ * NULL on memory error.
+ */
+static struct mld_group *
+mld6_new_group(struct netif *ifp, const ip6_addr_t *addr)
+{
+ struct mld_group *group;
+
+ group = (struct mld_group *)memp_malloc(MEMP_MLD6_GROUP);
+ if (group != NULL) {
+ ip6_addr_set(&(group->group_address), addr);
+ group->timer = 0; /* Not running */
+ group->group_state = MLD6_GROUP_IDLE_MEMBER;
+ group->last_reporter_flag = 0;
+ group->use = 0;
+ group->next = netif_mld6_data(ifp);
+
+ netif_set_client_data(ifp, LWIP_NETIF_CLIENT_DATA_INDEX_MLD6, group);
+ }
+
+ return group;
+}
+
+/**
+ * Remove a group from the mld_group_list, but do not free it yet
+ *
+ * @param group the group to remove
+ * @return ERR_OK if group was removed from the list, an err_t otherwise
+ */
+static err_t
+mld6_remove_group(struct netif *netif, struct mld_group *group)
+{
+ err_t err = ERR_OK;
+
+ /* Is it the first group? */
+ if (netif_mld6_data(netif) == group) {
+ netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_MLD6, group->next);
+ } else {
+ /* look for group further down the list */
+ struct mld_group *tmpGroup;
+ for (tmpGroup = netif_mld6_data(netif); tmpGroup != NULL; tmpGroup = tmpGroup->next) {
+ if (tmpGroup->next == group) {
+ tmpGroup->next = group->next;
+ break;
+ }
+ }
+ /* Group not find group */
+ if (tmpGroup == NULL) {
+ err = ERR_ARG;
+ }
+ }
+
+ return err;
+}
+
+
+/**
+ * Process an input MLD message. Called by icmp6_input.
+ *
+ * @param p the mld packet, p->payload pointing to the icmpv6 header
+ * @param inp the netif on which this packet was received
+ */
+void
+mld6_input(struct pbuf *p, struct netif *inp)
+{
+ struct mld_header *mld_hdr;
+ struct mld_group *group;
+
+ MLD6_STATS_INC(mld6.recv);
+
+ /* Check that mld header fits in packet. */
+ if (p->len < sizeof(struct mld_header)) {
+ /* @todo debug message */
+ pbuf_free(p);
+ MLD6_STATS_INC(mld6.lenerr);
+ MLD6_STATS_INC(mld6.drop);
+ return;
+ }
+
+ mld_hdr = (struct mld_header *)p->payload;
+
+ switch (mld_hdr->type) {
+ case ICMP6_TYPE_MLQ: /* Multicast listener query. */
+ /* Is it a general query? */
+ if (ip6_addr_isallnodes_linklocal(ip6_current_dest_addr()) &&
+ ip6_addr_isany(&(mld_hdr->multicast_address))) {
+ MLD6_STATS_INC(mld6.rx_general);
+ /* Report all groups, except all nodes group, and if-local groups. */
+ group = netif_mld6_data(inp);
+ while (group != NULL) {
+ if ((!(ip6_addr_ismulticast_iflocal(&(group->group_address)))) &&
+ (!(ip6_addr_isallnodes_linklocal(&(group->group_address))))) {
+ mld6_delayed_report(group, mld_hdr->max_resp_delay);
+ }
+ group = group->next;
+ }
+ } else {
+ /* Have we joined this group?
+ * We use IP6 destination address to have a memory aligned copy.
+ * mld_hdr->multicast_address should be the same. */
+ MLD6_STATS_INC(mld6.rx_group);
+ group = mld6_lookfor_group(inp, ip6_current_dest_addr());
+ if (group != NULL) {
+ /* Schedule a report. */
+ mld6_delayed_report(group, mld_hdr->max_resp_delay);
+ }
+ }
+ break; /* ICMP6_TYPE_MLQ */
+ case ICMP6_TYPE_MLR: /* Multicast listener report. */
+ /* Have we joined this group?
+ * We use IP6 destination address to have a memory aligned copy.
+ * mld_hdr->multicast_address should be the same. */
+ MLD6_STATS_INC(mld6.rx_report);
+ group = mld6_lookfor_group(inp, ip6_current_dest_addr());
+ if (group != NULL) {
+ /* If we are waiting to report, cancel it. */
+ if (group->group_state == MLD6_GROUP_DELAYING_MEMBER) {
+ group->timer = 0; /* stopped */
+ group->group_state = MLD6_GROUP_IDLE_MEMBER;
+ group->last_reporter_flag = 0;
+ }
+ }
+ break; /* ICMP6_TYPE_MLR */
+ case ICMP6_TYPE_MLD: /* Multicast listener done. */
+ /* Do nothing, router will query us. */
+ break; /* ICMP6_TYPE_MLD */
+ default:
+ MLD6_STATS_INC(mld6.proterr);
+ MLD6_STATS_INC(mld6.drop);
+ break;
+ }
+
+ pbuf_free(p);
+}
+
+/**
+ * @ingroup mld6
+ * Join a group on a network interface.
+ *
+ * @param srcaddr ipv6 address of the network interface which should
+ * join a new group. If IP6_ADDR_ANY, join on all netifs
+ * @param groupaddr the ipv6 address of the group to join
+ * @return ERR_OK if group was joined on the netif(s), an err_t otherwise
+ */
+err_t
+mld6_joingroup(const ip6_addr_t *srcaddr, const ip6_addr_t *groupaddr)
+{
+ err_t err = ERR_VAL; /* no matching interface */
+ struct netif *netif;
+
+ /* loop through netif's */
+ netif = netif_list;
+ while (netif != NULL) {
+ /* Should we join this interface ? */
+ if (ip6_addr_isany(srcaddr) ||
+ netif_get_ip6_addr_match(netif, srcaddr) >= 0) {
+ err = mld6_joingroup_netif(netif, groupaddr);
+ if (err != ERR_OK) {
+ return err;
+ }
+ }
+
+ /* proceed to next network interface */
+ netif = netif->next;
+ }
+
+ return err;
+}
+
+/**
+ * @ingroup mld6
+ * Join a group on a network interface.
+ *
+ * @param netif the network interface which should join a new group.
+ * @param groupaddr the ipv6 address of the group to join
+ * @return ERR_OK if group was joined on the netif, an err_t otherwise
+ */
+err_t
+mld6_joingroup_netif(struct netif *netif, const ip6_addr_t *groupaddr)
+{
+ struct mld_group *group;
+
+ /* find group or create a new one if not found */
+ group = mld6_lookfor_group(netif, groupaddr);
+
+ if (group == NULL) {
+ /* Joining a new group. Create a new group entry. */
+ group = mld6_new_group(netif, groupaddr);
+ if (group == NULL) {
+ return ERR_MEM;
+ }
+
+ /* Activate this address on the MAC layer. */
+ if (netif->mld_mac_filter != NULL) {
+ netif->mld_mac_filter(netif, groupaddr, NETIF_ADD_MAC_FILTER);
+ }
+
+ /* Report our membership. */
+ MLD6_STATS_INC(mld6.tx_report);
+ mld6_send(netif, group, ICMP6_TYPE_MLR);
+ mld6_delayed_report(group, MLD6_JOIN_DELAYING_MEMBER_TMR_MS);
+ }
+
+ /* Increment group use */
+ group->use++;
+ return ERR_OK;
+}
+
+/**
+ * @ingroup mld6
+ * Leave a group on a network interface.
+ *
+ * @param srcaddr ipv6 address of the network interface which should
+ * leave the group. If IP6_ISANY, leave on all netifs
+ * @param groupaddr the ipv6 address of the group to leave
+ * @return ERR_OK if group was left on the netif(s), an err_t otherwise
+ */
+err_t
+mld6_leavegroup(const ip6_addr_t *srcaddr, const ip6_addr_t *groupaddr)
+{
+ err_t err = ERR_VAL; /* no matching interface */
+ struct netif *netif;
+
+ /* loop through netif's */
+ netif = netif_list;
+ while (netif != NULL) {
+ /* Should we leave this interface ? */
+ if (ip6_addr_isany(srcaddr) ||
+ netif_get_ip6_addr_match(netif, srcaddr) >= 0) {
+ err_t res = mld6_leavegroup_netif(netif, groupaddr);
+ if (err != ERR_OK) {
+ /* Store this result if we have not yet gotten a success */
+ err = res;
+ }
+ }
+ /* proceed to next network interface */
+ netif = netif->next;
+ }
+
+ return err;
+}
+
+/**
+ * @ingroup mld6
+ * Leave a group on a network interface.
+ *
+ * @param netif the network interface which should leave the group.
+ * @param groupaddr the ipv6 address of the group to leave
+ * @return ERR_OK if group was left on the netif, an err_t otherwise
+ */
+err_t
+mld6_leavegroup_netif(struct netif *netif, const ip6_addr_t *groupaddr)
+{
+ struct mld_group *group;
+
+ /* find group */
+ group = mld6_lookfor_group(netif, groupaddr);
+
+ if (group != NULL) {
+ /* Leave if there is no other use of the group */
+ if (group->use <= 1) {
+ /* Remove the group from the list */
+ mld6_remove_group(netif, group);
+
+ /* If we are the last reporter for this group */
+ if (group->last_reporter_flag) {
+ MLD6_STATS_INC(mld6.tx_leave);
+ mld6_send(netif, group, ICMP6_TYPE_MLD);
+ }
+
+ /* Disable the group at the MAC level */
+ if (netif->mld_mac_filter != NULL) {
+ netif->mld_mac_filter(netif, groupaddr, NETIF_DEL_MAC_FILTER);
+ }
+
+ /* free group struct */
+ memp_free(MEMP_MLD6_GROUP, group);
+ } else {
+ /* Decrement group use */
+ group->use--;
+ }
+
+ /* Left group */
+ return ERR_OK;
+ }
+
+ /* Group not found */
+ return ERR_VAL;
+}
+
+
+/**
+ * Periodic timer for mld processing. Must be called every
+ * MLD6_TMR_INTERVAL milliseconds (100).
+ *
+ * When a delaying member expires, a membership report is sent.
+ */
+void
+mld6_tmr(void)
+{
+ struct netif *netif = netif_list;
+
+ while (netif != NULL) {
+ struct mld_group *group = netif_mld6_data(netif);
+
+ while (group != NULL) {
+ if (group->timer > 0) {
+ group->timer--;
+ if (group->timer == 0) {
+ /* If the state is MLD6_GROUP_DELAYING_MEMBER then we send a report for this group */
+ if (group->group_state == MLD6_GROUP_DELAYING_MEMBER) {
+ MLD6_STATS_INC(mld6.tx_report);
+ mld6_send(netif, group, ICMP6_TYPE_MLR);
+ group->group_state = MLD6_GROUP_IDLE_MEMBER;
+ }
+ }
+ }
+ group = group->next;
+ }
+ netif = netif->next;
+ }
+}
+
+/**
+ * Schedule a delayed membership report for a group
+ *
+ * @param group the mld_group for which "delaying" membership report
+ * should be sent
+ * @param maxresp the max resp delay provided in the query
+ */
+static void
+mld6_delayed_report(struct mld_group *group, u16_t maxresp)
+{
+ /* Convert maxresp from milliseconds to tmr ticks */
+ maxresp = maxresp / MLD6_TMR_INTERVAL;
+ if (maxresp == 0) {
+ maxresp = 1;
+ }
+
+#ifdef LWIP_RAND
+ /* Randomize maxresp. (if LWIP_RAND is supported) */
+ maxresp = LWIP_RAND() % maxresp;
+ if (maxresp == 0) {
+ maxresp = 1;
+ }
+#endif /* LWIP_RAND */
+
+ /* Apply timer value if no report has been scheduled already. */
+ if ((group->group_state == MLD6_GROUP_IDLE_MEMBER) ||
+ ((group->group_state == MLD6_GROUP_DELAYING_MEMBER) &&
+ ((group->timer == 0) || (maxresp < group->timer)))) {
+ group->timer = maxresp;
+ group->group_state = MLD6_GROUP_DELAYING_MEMBER;
+ }
+}
+
+/**
+ * Send a MLD message (report or done).
+ *
+ * An IPv6 hop-by-hop options header with a router alert option
+ * is prepended.
+ *
+ * @param group the group to report or quit
+ * @param type ICMP6_TYPE_MLR (report) or ICMP6_TYPE_MLD (done)
+ */
+static void
+mld6_send(struct netif *netif, struct mld_group *group, u8_t type)
+{
+ struct mld_header *mld_hdr;
+ struct pbuf *p;
+ const ip6_addr_t *src_addr;
+
+ /* Allocate a packet. Size is MLD header + IPv6 Hop-by-hop options header. */
+ p = pbuf_alloc(PBUF_IP, sizeof(struct mld_header) + sizeof(struct ip6_hbh_hdr), PBUF_RAM);
+ if (p == NULL) {
+ MLD6_STATS_INC(mld6.memerr);
+ return;
+ }
+
+ /* Move to make room for Hop-by-hop options header. */
+ if (pbuf_header(p, -IP6_HBH_HLEN)) {
+ pbuf_free(p);
+ MLD6_STATS_INC(mld6.lenerr);
+ return;
+ }
+
+ /* Select our source address. */
+ if (!ip6_addr_isvalid(netif_ip6_addr_state(netif, 0))) {
+ /* This is a special case, when we are performing duplicate address detection.
+ * We must join the multicast group, but we don't have a valid address yet. */
+ src_addr = IP6_ADDR_ANY6;
+ } else {
+ /* Use link-local address as source address. */
+ src_addr = netif_ip6_addr(netif, 0);
+ }
+
+ /* MLD message header pointer. */
+ mld_hdr = (struct mld_header *)p->payload;
+
+ /* Set fields. */
+ mld_hdr->type = type;
+ mld_hdr->code = 0;
+ mld_hdr->chksum = 0;
+ mld_hdr->max_resp_delay = 0;
+ mld_hdr->reserved = 0;
+ ip6_addr_set(&(mld_hdr->multicast_address), &(group->group_address));
+
+#if CHECKSUM_GEN_ICMP6
+ IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_ICMP6) {
+ mld_hdr->chksum = ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->len,
+ src_addr, &(group->group_address));
+ }
+#endif /* CHECKSUM_GEN_ICMP6 */
+
+ /* Add hop-by-hop headers options: router alert with MLD value. */
+ ip6_options_add_hbh_ra(p, IP6_NEXTH_ICMP6, IP6_ROUTER_ALERT_VALUE_MLD);
+
+ if (type == ICMP6_TYPE_MLR) {
+ /* Remember we were the last to report */
+ group->last_reporter_flag = 1;
+ }
+
+ /* Send the packet out. */
+ MLD6_STATS_INC(mld6.xmit);
+ ip6_output_if(p, (ip6_addr_isany(src_addr)) ? NULL : src_addr, &(group->group_address),
+ MLD6_HL, 0, IP6_NEXTH_HOPBYHOP, netif);
+ pbuf_free(p);
+}
+
+#endif /* LWIP_IPV6 */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/ipv6/nd6.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/ipv6/nd6.c
new file mode 100644
index 0000000..4c07c87
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/ipv6/nd6.c
@@ -0,0 +1,2108 @@
+/**
+ * @file
+ *
+ * Neighbor discovery and stateless address autoconfiguration for IPv6.
+ * Aims to be compliant with RFC 4861 (Neighbor discovery) and RFC 4862
+ * (Address autoconfiguration).
+ */
+
+/*
+ * Copyright (c) 2010 Inico Technologies Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Ivan Delamer <delamer@inicotech.com>
+ *
+ *
+ * Please coordinate changes and requests with Ivan Delamer
+ * <delamer@inicotech.com>
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/nd6.h"
+#include "lwip/priv/nd6_priv.h"
+#include "lwip/prot/nd6.h"
+#include "lwip/prot/icmp6.h"
+#include "lwip/pbuf.h"
+#include "lwip/mem.h"
+#include "lwip/memp.h"
+#include "lwip/ip6.h"
+#include "lwip/ip6_addr.h"
+#include "lwip/inet_chksum.h"
+#include "lwip/netif.h"
+#include "lwip/icmp6.h"
+#include "lwip/mld6.h"
+#include "lwip/ip.h"
+#include "lwip/stats.h"
+#include "lwip/dns.h"
+
+#include <string.h>
+
+#ifdef LWIP_HOOK_FILENAME
+#include LWIP_HOOK_FILENAME
+#endif
+
+#if LWIP_IPV6_DUP_DETECT_ATTEMPTS > IP6_ADDR_TENTATIVE_COUNT_MASK
+#error LWIP_IPV6_DUP_DETECT_ATTEMPTS > IP6_ADDR_TENTATIVE_COUNT_MASK
+#endif
+
+/* Router tables. */
+struct nd6_neighbor_cache_entry neighbor_cache[LWIP_ND6_NUM_NEIGHBORS];
+struct nd6_destination_cache_entry destination_cache[LWIP_ND6_NUM_DESTINATIONS];
+struct nd6_prefix_list_entry prefix_list[LWIP_ND6_NUM_PREFIXES];
+struct nd6_router_list_entry default_router_list[LWIP_ND6_NUM_ROUTERS];
+
+/* Default values, can be updated by a RA message. */
+u32_t reachable_time = LWIP_ND6_REACHABLE_TIME;
+u32_t retrans_timer = LWIP_ND6_RETRANS_TIMER; /* @todo implement this value in timer */
+
+/* Index for cache entries. */
+static u8_t nd6_cached_neighbor_index;
+static u8_t nd6_cached_destination_index;
+
+/* Multicast address holder. */
+static ip6_addr_t multicast_address;
+
+/* Static buffer to parse RA packet options (size of a prefix option, biggest option) */
+static u8_t nd6_ra_buffer[sizeof(struct prefix_option)];
+
+/* Forward declarations. */
+static s8_t nd6_find_neighbor_cache_entry(const ip6_addr_t *ip6addr);
+static s8_t nd6_new_neighbor_cache_entry(void);
+static void nd6_free_neighbor_cache_entry(s8_t i);
+static s8_t nd6_find_destination_cache_entry(const ip6_addr_t *ip6addr);
+static s8_t nd6_new_destination_cache_entry(void);
+static s8_t nd6_is_prefix_in_netif(const ip6_addr_t *ip6addr, struct netif *netif);
+static s8_t nd6_select_router(const ip6_addr_t *ip6addr, struct netif *netif);
+static s8_t nd6_get_router(const ip6_addr_t *router_addr, struct netif *netif);
+static s8_t nd6_new_router(const ip6_addr_t *router_addr, struct netif *netif);
+static s8_t nd6_get_onlink_prefix(ip6_addr_t *prefix, struct netif *netif);
+static s8_t nd6_new_onlink_prefix(ip6_addr_t *prefix, struct netif *netif);
+static s8_t nd6_get_next_hop_entry(const ip6_addr_t *ip6addr, struct netif *netif);
+static err_t nd6_queue_packet(s8_t neighbor_index, struct pbuf *q);
+
+#define ND6_SEND_FLAG_MULTICAST_DEST 0x01
+#define ND6_SEND_FLAG_ALLNODES_DEST 0x02
+static void nd6_send_ns(struct netif *netif, const ip6_addr_t *target_addr, u8_t flags);
+static void nd6_send_na(struct netif *netif, const ip6_addr_t *target_addr, u8_t flags);
+static void nd6_send_neighbor_cache_probe(struct nd6_neighbor_cache_entry *entry, u8_t flags);
+#if LWIP_IPV6_SEND_ROUTER_SOLICIT
+static err_t nd6_send_rs(struct netif *netif);
+#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */
+
+#if LWIP_ND6_QUEUEING
+static void nd6_free_q(struct nd6_q_entry *q);
+#else /* LWIP_ND6_QUEUEING */
+#define nd6_free_q(q) pbuf_free(q)
+#endif /* LWIP_ND6_QUEUEING */
+static void nd6_send_q(s8_t i);
+
+
+/**
+ * Process an incoming neighbor discovery message
+ *
+ * @param p the nd packet, p->payload pointing to the icmpv6 header
+ * @param inp the netif on which this packet was received
+ */
+void
+nd6_input(struct pbuf *p, struct netif *inp)
+{
+ u8_t msg_type;
+ s8_t i;
+
+ ND6_STATS_INC(nd6.recv);
+
+ msg_type = *((u8_t *)p->payload);
+ switch (msg_type) {
+ case ICMP6_TYPE_NA: /* Neighbor Advertisement. */
+ {
+ struct na_header *na_hdr;
+ struct lladdr_option *lladdr_opt;
+
+ /* Check that na header fits in packet. */
+ if (p->len < (sizeof(struct na_header))) {
+ /* @todo debug message */
+ pbuf_free(p);
+ ND6_STATS_INC(nd6.lenerr);
+ ND6_STATS_INC(nd6.drop);
+ return;
+ }
+
+ na_hdr = (struct na_header *)p->payload;
+
+ /* Unsolicited NA?*/
+ if (ip6_addr_ismulticast(ip6_current_dest_addr())) {
+ ip6_addr_t target_address;
+
+ /* This is an unsolicited NA.
+ * link-layer changed?
+ * part of DAD mechanism? */
+
+ /* Create an aligned copy. */
+ ip6_addr_set(&target_address, &(na_hdr->target_address));
+
+#if LWIP_IPV6_DUP_DETECT_ATTEMPTS
+ /* If the target address matches this netif, it is a DAD response. */
+ for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+ if (!ip6_addr_isinvalid(netif_ip6_addr_state(inp, i)) &&
+ ip6_addr_cmp(&target_address, netif_ip6_addr(inp, i))) {
+ /* We are using a duplicate address. */
+ netif_ip6_addr_set_state(inp, i, IP6_ADDR_INVALID);
+
+#if LWIP_IPV6_AUTOCONFIG
+ /* Check to see if this address was autoconfigured. */
+ if (!ip6_addr_islinklocal(&target_address)) {
+ i = nd6_get_onlink_prefix(&target_address, inp);
+ if (i >= 0) {
+ /* Mark this prefix as duplicate, so that we don't use it
+ * to generate this address again. */
+ prefix_list[i].flags |= ND6_PREFIX_AUTOCONFIG_ADDRESS_DUPLICATE;
+ }
+ }
+#endif /* LWIP_IPV6_AUTOCONFIG */
+
+ pbuf_free(p);
+ return;
+ }
+ }
+#endif /* LWIP_IPV6_DUP_DETECT_ATTEMPTS */
+
+ /* Check that link-layer address option also fits in packet. */
+ if (p->len < (sizeof(struct na_header) + 2)) {
+ /* @todo debug message */
+ pbuf_free(p);
+ ND6_STATS_INC(nd6.lenerr);
+ ND6_STATS_INC(nd6.drop);
+ return;
+ }
+
+ lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct na_header));
+
+ if (p->len < (sizeof(struct na_header) + (lladdr_opt->length << 3))) {
+ /* @todo debug message */
+ pbuf_free(p);
+ ND6_STATS_INC(nd6.lenerr);
+ ND6_STATS_INC(nd6.drop);
+ return;
+ }
+
+ /* This is an unsolicited NA, most likely there was a LLADDR change. */
+ i = nd6_find_neighbor_cache_entry(&target_address);
+ if (i >= 0) {
+ if (na_hdr->flags & ND6_FLAG_OVERRIDE) {
+ MEMCPY(neighbor_cache[i].lladdr, lladdr_opt->addr, inp->hwaddr_len);
+ }
+ }
+ } else {
+ ip6_addr_t target_address;
+
+ /* This is a solicited NA.
+ * neighbor address resolution response?
+ * neighbor unreachability detection response? */
+
+ /* Create an aligned copy. */
+ ip6_addr_set(&target_address, &(na_hdr->target_address));
+
+ /* Find the cache entry corresponding to this na. */
+ i = nd6_find_neighbor_cache_entry(&target_address);
+ if (i < 0) {
+ /* We no longer care about this target address. drop it. */
+ pbuf_free(p);
+ return;
+ }
+
+ /* Update cache entry. */
+ if ((na_hdr->flags & ND6_FLAG_OVERRIDE) ||
+ (neighbor_cache[i].state == ND6_INCOMPLETE)) {
+ /* Check that link-layer address option also fits in packet. */
+ if (p->len < (sizeof(struct na_header) + 2)) {
+ /* @todo debug message */
+ pbuf_free(p);
+ ND6_STATS_INC(nd6.lenerr);
+ ND6_STATS_INC(nd6.drop);
+ return;
+ }
+
+ lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct na_header));
+
+ if (p->len < (sizeof(struct na_header) + (lladdr_opt->length << 3))) {
+ /* @todo debug message */
+ pbuf_free(p);
+ ND6_STATS_INC(nd6.lenerr);
+ ND6_STATS_INC(nd6.drop);
+ return;
+ }
+
+ MEMCPY(neighbor_cache[i].lladdr, lladdr_opt->addr, inp->hwaddr_len);
+ }
+
+ neighbor_cache[i].netif = inp;
+ neighbor_cache[i].state = ND6_REACHABLE;
+ neighbor_cache[i].counter.reachable_time = reachable_time;
+
+ /* Send queued packets, if any. */
+ if (neighbor_cache[i].q != NULL) {
+ nd6_send_q(i);
+ }
+ }
+
+ break; /* ICMP6_TYPE_NA */
+ }
+ case ICMP6_TYPE_NS: /* Neighbor solicitation. */
+ {
+ struct ns_header *ns_hdr;
+ struct lladdr_option *lladdr_opt;
+ u8_t accepted;
+
+ /* Check that ns header fits in packet. */
+ if (p->len < sizeof(struct ns_header)) {
+ /* @todo debug message */
+ pbuf_free(p);
+ ND6_STATS_INC(nd6.lenerr);
+ ND6_STATS_INC(nd6.drop);
+ return;
+ }
+
+ ns_hdr = (struct ns_header *)p->payload;
+
+ /* Check if there is a link-layer address provided. Only point to it if in this buffer. */
+ if (p->len >= (sizeof(struct ns_header) + 2)) {
+ lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct ns_header));
+ if (p->len < (sizeof(struct ns_header) + (lladdr_opt->length << 3))) {
+ lladdr_opt = NULL;
+ }
+ } else {
+ lladdr_opt = NULL;
+ }
+
+ /* Check if the target address is configured on the receiving netif. */
+ accepted = 0;
+ for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; ++i) {
+ if ((ip6_addr_isvalid(netif_ip6_addr_state(inp, i)) ||
+ (ip6_addr_istentative(netif_ip6_addr_state(inp, i)) &&
+ ip6_addr_isany(ip6_current_src_addr()))) &&
+ ip6_addr_cmp(&(ns_hdr->target_address), netif_ip6_addr(inp, i))) {
+ accepted = 1;
+ break;
+ }
+ }
+
+ /* NS not for us? */
+ if (!accepted) {
+ pbuf_free(p);
+ return;
+ }
+
+ /* Check for ANY address in src (DAD algorithm). */
+ if (ip6_addr_isany(ip6_current_src_addr())) {
+ /* Sender is validating this address. */
+ for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; ++i) {
+ if (!ip6_addr_isinvalid(netif_ip6_addr_state(inp, i)) &&
+ ip6_addr_cmp(&(ns_hdr->target_address), netif_ip6_addr(inp, i))) {
+ /* Send a NA back so that the sender does not use this address. */
+ nd6_send_na(inp, netif_ip6_addr(inp, i), ND6_FLAG_OVERRIDE | ND6_SEND_FLAG_ALLNODES_DEST);
+ if (ip6_addr_istentative(netif_ip6_addr_state(inp, i))) {
+ /* We shouldn't use this address either. */
+ netif_ip6_addr_set_state(inp, i, IP6_ADDR_INVALID);
+ }
+ }
+ }
+ } else {
+ ip6_addr_t target_address;
+
+ /* Sender is trying to resolve our address. */
+ /* Verify that they included their own link-layer address. */
+ if (lladdr_opt == NULL) {
+ /* Not a valid message. */
+ pbuf_free(p);
+ ND6_STATS_INC(nd6.proterr);
+ ND6_STATS_INC(nd6.drop);
+ return;
+ }
+
+ i = nd6_find_neighbor_cache_entry(ip6_current_src_addr());
+ if (i>= 0) {
+ /* We already have a record for the solicitor. */
+ if (neighbor_cache[i].state == ND6_INCOMPLETE) {
+ neighbor_cache[i].netif = inp;
+ MEMCPY(neighbor_cache[i].lladdr, lladdr_opt->addr, inp->hwaddr_len);
+
+ /* Delay probe in case we get confirmation of reachability from upper layer (TCP). */
+ neighbor_cache[i].state = ND6_DELAY;
+ neighbor_cache[i].counter.delay_time = LWIP_ND6_DELAY_FIRST_PROBE_TIME / ND6_TMR_INTERVAL;
+ }
+ } else {
+ /* Add their IPv6 address and link-layer address to neighbor cache.
+ * We will need it at least to send a unicast NA message, but most
+ * likely we will also be communicating with this node soon. */
+ i = nd6_new_neighbor_cache_entry();
+ if (i < 0) {
+ /* We couldn't assign a cache entry for this neighbor.
+ * we won't be able to reply. drop it. */
+ pbuf_free(p);
+ ND6_STATS_INC(nd6.memerr);
+ return;
+ }
+ neighbor_cache[i].netif = inp;
+ MEMCPY(neighbor_cache[i].lladdr, lladdr_opt->addr, inp->hwaddr_len);
+ ip6_addr_set(&(neighbor_cache[i].next_hop_address), ip6_current_src_addr());
+
+ /* Receiving a message does not prove reachability: only in one direction.
+ * Delay probe in case we get confirmation of reachability from upper layer (TCP). */
+ neighbor_cache[i].state = ND6_DELAY;
+ neighbor_cache[i].counter.delay_time = LWIP_ND6_DELAY_FIRST_PROBE_TIME / ND6_TMR_INTERVAL;
+ }
+
+ /* Create an aligned copy. */
+ ip6_addr_set(&target_address, &(ns_hdr->target_address));
+
+ /* Send back a NA for us. Allocate the reply pbuf. */
+ nd6_send_na(inp, &target_address, ND6_FLAG_SOLICITED | ND6_FLAG_OVERRIDE);
+ }
+
+ break; /* ICMP6_TYPE_NS */
+ }
+ case ICMP6_TYPE_RA: /* Router Advertisement. */
+ {
+ struct ra_header *ra_hdr;
+ u8_t *buffer; /* Used to copy options. */
+ u16_t offset;
+#if LWIP_ND6_RDNSS_MAX_DNS_SERVERS
+ /* There can by multiple RDNSS options per RA */
+ u8_t rdnss_server_idx = 0;
+#endif /* LWIP_ND6_RDNSS_MAX_DNS_SERVERS */
+
+ /* Check that RA header fits in packet. */
+ if (p->len < sizeof(struct ra_header)) {
+ /* @todo debug message */
+ pbuf_free(p);
+ ND6_STATS_INC(nd6.lenerr);
+ ND6_STATS_INC(nd6.drop);
+ return;
+ }
+
+ ra_hdr = (struct ra_header *)p->payload;
+
+ /* If we are sending RS messages, stop. */
+#if LWIP_IPV6_SEND_ROUTER_SOLICIT
+ /* ensure at least one solicitation is sent */
+ if ((inp->rs_count < LWIP_ND6_MAX_MULTICAST_SOLICIT) ||
+ (nd6_send_rs(inp) == ERR_OK)) {
+ inp->rs_count = 0;
+ }
+#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */
+
+ /* Get the matching default router entry. */
+ i = nd6_get_router(ip6_current_src_addr(), inp);
+ if (i < 0) {
+ /* Create a new router entry. */
+ i = nd6_new_router(ip6_current_src_addr(), inp);
+ }
+
+ if (i < 0) {
+ /* Could not create a new router entry. */
+ pbuf_free(p);
+ ND6_STATS_INC(nd6.memerr);
+ return;
+ }
+
+ /* Re-set invalidation timer. */
+ default_router_list[i].invalidation_timer = lwip_htons(ra_hdr->router_lifetime);
+
+ /* Re-set default timer values. */
+#if LWIP_ND6_ALLOW_RA_UPDATES
+ if (ra_hdr->retrans_timer > 0) {
+ retrans_timer = lwip_htonl(ra_hdr->retrans_timer);
+ }
+ if (ra_hdr->reachable_time > 0) {
+ reachable_time = lwip_htonl(ra_hdr->reachable_time);
+ }
+#endif /* LWIP_ND6_ALLOW_RA_UPDATES */
+
+ /* @todo set default hop limit... */
+ /* ra_hdr->current_hop_limit;*/
+
+ /* Update flags in local entry (incl. preference). */
+ default_router_list[i].flags = ra_hdr->flags;
+
+ /* Offset to options. */
+ offset = sizeof(struct ra_header);
+
+ /* Process each option. */
+ while ((p->tot_len - offset) > 0) {
+ if (p->len == p->tot_len) {
+ /* no need to copy from contiguous pbuf */
+ buffer = &((u8_t*)p->payload)[offset];
+ } else {
+ buffer = nd6_ra_buffer;
+ if (pbuf_copy_partial(p, buffer, sizeof(struct prefix_option), offset) != sizeof(struct prefix_option)) {
+ pbuf_free(p);
+ ND6_STATS_INC(nd6.lenerr);
+ ND6_STATS_INC(nd6.drop);
+ return;
+ }
+ }
+ if (buffer[1] == 0) {
+ /* zero-length extension. drop packet */
+ pbuf_free(p);
+ ND6_STATS_INC(nd6.lenerr);
+ ND6_STATS_INC(nd6.drop);
+ return;
+ }
+ switch (buffer[0]) {
+ case ND6_OPTION_TYPE_SOURCE_LLADDR:
+ {
+ struct lladdr_option *lladdr_opt;
+ lladdr_opt = (struct lladdr_option *)buffer;
+ if ((default_router_list[i].neighbor_entry != NULL) &&
+ (default_router_list[i].neighbor_entry->state == ND6_INCOMPLETE)) {
+ SMEMCPY(default_router_list[i].neighbor_entry->lladdr, lladdr_opt->addr, inp->hwaddr_len);
+ default_router_list[i].neighbor_entry->state = ND6_REACHABLE;
+ default_router_list[i].neighbor_entry->counter.reachable_time = reachable_time;
+ }
+ break;
+ }
+ case ND6_OPTION_TYPE_MTU:
+ {
+ struct mtu_option *mtu_opt;
+ mtu_opt = (struct mtu_option *)buffer;
+ if (lwip_htonl(mtu_opt->mtu) >= 1280) {
+#if LWIP_ND6_ALLOW_RA_UPDATES
+ inp->mtu = (u16_t)lwip_htonl(mtu_opt->mtu);
+#endif /* LWIP_ND6_ALLOW_RA_UPDATES */
+ }
+ break;
+ }
+ case ND6_OPTION_TYPE_PREFIX_INFO:
+ {
+ struct prefix_option *prefix_opt;
+ prefix_opt = (struct prefix_option *)buffer;
+
+ #if LWIP_BTLE_6LOWPAN
+ if ((prefix_opt->prefix_length == 64) &&
+ !ip6_addr_islinklocal(&(prefix_opt->prefix))) {
+ #else //LWIP_BTLE_6LOWPAN
+ if ((prefix_opt->flags & ND6_PREFIX_FLAG_ON_LINK) &&
+ (prefix_opt->prefix_length == 64) &&
+ !ip6_addr_islinklocal(&(prefix_opt->prefix))) {
+ #endif //LWIP_BTLE_6LOWPAN
+
+ /* Add to on-link prefix list. */
+ s8_t prefix;
+ ip6_addr_t prefix_addr;
+
+ /* Get a memory-aligned copy of the prefix. */
+ ip6_addr_set(&prefix_addr, &(prefix_opt->prefix));
+
+ /* find cache entry for this prefix. */
+ prefix = nd6_get_onlink_prefix(&prefix_addr, inp);
+ if (prefix < 0) {
+ /* Create a new cache entry. */
+ prefix = nd6_new_onlink_prefix(&prefix_addr, inp);
+ }
+ if (prefix >= 0) {
+ prefix_list[prefix].invalidation_timer = lwip_htonl(prefix_opt->valid_lifetime);
+
+#if LWIP_IPV6_AUTOCONFIG
+ if (prefix_opt->flags & ND6_PREFIX_FLAG_AUTONOMOUS) {
+ /* Mark prefix as autonomous, so that address autoconfiguration can take place.
+ * Only OR flag, so that we don't over-write other flags (such as ADDRESS_DUPLICATE)*/
+ prefix_list[prefix].flags |= ND6_PREFIX_AUTOCONFIG_AUTONOMOUS;
+ }
+#endif /* LWIP_IPV6_AUTOCONFIG */
+ }
+ }
+
+ break;
+ }
+ case ND6_OPTION_TYPE_ROUTE_INFO:
+ /* @todo implement preferred routes.
+ struct route_option * route_opt;
+ route_opt = (struct route_option *)buffer;*/
+
+ break;
+#if LWIP_ND6_RDNSS_MAX_DNS_SERVERS
+ case ND6_OPTION_TYPE_RDNSS:
+ {
+ u8_t num, n;
+ struct rdnss_option * rdnss_opt;
+
+ rdnss_opt = (struct rdnss_option *)buffer;
+ num = (rdnss_opt->length - 1) / 2;
+ for (n = 0; (rdnss_server_idx < DNS_MAX_SERVERS) && (n < num); n++) {
+ ip_addr_t rdnss_address;
+
+ /* Get a memory-aligned copy of the prefix. */
+ ip_addr_copy_from_ip6(rdnss_address, rdnss_opt->rdnss_address[n]);
+
+ if (htonl(rdnss_opt->lifetime) > 0) {
+ /* TODO implement Lifetime > 0 */
+ dns_setserver(rdnss_server_idx++, &rdnss_address);
+ } else {
+ /* TODO implement DNS removal in dns.c */
+ u8_t s;
+ for (s = 0; s < DNS_MAX_SERVERS; s++) {
+ const ip_addr_t *addr = dns_getserver(s);
+ if(ip_addr_cmp(addr, &rdnss_address)) {
+ dns_setserver(s, NULL);
+ }
+ }
+ }
+ }
+ break;
+ }
+#endif /* LWIP_ND6_RDNSS_MAX_DNS_SERVERS */
+ default:
+ /* Unrecognized option, abort. */
+ ND6_STATS_INC(nd6.proterr);
+ break;
+ }
+ /* option length is checked earlier to be non-zero to make sure loop ends */
+ offset += 8 * ((u16_t)buffer[1]);
+ }
+
+ break; /* ICMP6_TYPE_RA */
+ }
+ case ICMP6_TYPE_RD: /* Redirect */
+ {
+ struct redirect_header *redir_hdr;
+ struct lladdr_option *lladdr_opt;
+ ip6_addr_t tmp;
+
+ /* Check that Redir header fits in packet. */
+ if (p->len < sizeof(struct redirect_header)) {
+ /* @todo debug message */
+ pbuf_free(p);
+ ND6_STATS_INC(nd6.lenerr);
+ ND6_STATS_INC(nd6.drop);
+ return;
+ }
+
+ redir_hdr = (struct redirect_header *)p->payload;
+
+ if (p->len >= (sizeof(struct redirect_header) + 2)) {
+ lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct redirect_header));
+ if (p->len < (sizeof(struct redirect_header) + (lladdr_opt->length << 3))) {
+ lladdr_opt = NULL;
+ }
+ } else {
+ lladdr_opt = NULL;
+ }
+
+ /* Copy original destination address to current source address, to have an aligned copy. */
+ ip6_addr_set(&tmp, &(redir_hdr->destination_address));
+
+ /* Find dest address in cache */
+ i = nd6_find_destination_cache_entry(&tmp);
+ if (i < 0) {
+ /* Destination not in cache, drop packet. */
+ pbuf_free(p);
+ return;
+ }
+
+ /* Set the new target address. */
+ ip6_addr_set(&(destination_cache[i].next_hop_addr), &(redir_hdr->target_address));
+
+ /* If Link-layer address of other router is given, try to add to neighbor cache. */
+ if (lladdr_opt != NULL) {
+ if (lladdr_opt->type == ND6_OPTION_TYPE_TARGET_LLADDR) {
+ /* Copy target address to current source address, to have an aligned copy. */
+ ip6_addr_set(&tmp, &(redir_hdr->target_address));
+
+ i = nd6_find_neighbor_cache_entry(&tmp);
+ if (i < 0) {
+ i = nd6_new_neighbor_cache_entry();
+ if (i >= 0) {
+ neighbor_cache[i].netif = inp;
+ MEMCPY(neighbor_cache[i].lladdr, lladdr_opt->addr, inp->hwaddr_len);
+ ip6_addr_set(&(neighbor_cache[i].next_hop_address), &tmp);
+
+ /* Receiving a message does not prove reachability: only in one direction.
+ * Delay probe in case we get confirmation of reachability from upper layer (TCP). */
+ neighbor_cache[i].state = ND6_DELAY;
+ neighbor_cache[i].counter.delay_time = LWIP_ND6_DELAY_FIRST_PROBE_TIME / ND6_TMR_INTERVAL;
+ }
+ }
+ if (i >= 0) {
+ if (neighbor_cache[i].state == ND6_INCOMPLETE) {
+ MEMCPY(neighbor_cache[i].lladdr, lladdr_opt->addr, inp->hwaddr_len);
+ /* Receiving a message does not prove reachability: only in one direction.
+ * Delay probe in case we get confirmation of reachability from upper layer (TCP). */
+ neighbor_cache[i].state = ND6_DELAY;
+ neighbor_cache[i].counter.delay_time = LWIP_ND6_DELAY_FIRST_PROBE_TIME / ND6_TMR_INTERVAL;
+ }
+ }
+ }
+ }
+ break; /* ICMP6_TYPE_RD */
+ }
+ case ICMP6_TYPE_PTB: /* Packet too big */
+ {
+ struct icmp6_hdr *icmp6hdr; /* Packet too big message */
+ struct ip6_hdr *ip6hdr; /* IPv6 header of the packet which caused the error */
+ u32_t pmtu;
+ ip6_addr_t tmp;
+
+ /* Check that ICMPv6 header + IPv6 header fit in payload */
+ if (p->len < (sizeof(struct icmp6_hdr) + IP6_HLEN)) {
+ /* drop short packets */
+ pbuf_free(p);
+ ND6_STATS_INC(nd6.lenerr);
+ ND6_STATS_INC(nd6.drop);
+ return;
+ }
+
+ icmp6hdr = (struct icmp6_hdr *)p->payload;
+ ip6hdr = (struct ip6_hdr *)((u8_t*)p->payload + sizeof(struct icmp6_hdr));
+
+ /* Copy original destination address to current source address, to have an aligned copy. */
+ ip6_addr_set(&tmp, &(ip6hdr->dest));
+
+ /* Look for entry in destination cache. */
+ i = nd6_find_destination_cache_entry(&tmp);
+ if (i < 0) {
+ /* Destination not in cache, drop packet. */
+ pbuf_free(p);
+ return;
+ }
+
+ /* Change the Path MTU. */
+ pmtu = lwip_htonl(icmp6hdr->data);
+ destination_cache[i].pmtu = (u16_t)LWIP_MIN(pmtu, 0xFFFF);
+
+ break; /* ICMP6_TYPE_PTB */
+ }
+
+ default:
+ ND6_STATS_INC(nd6.proterr);
+ ND6_STATS_INC(nd6.drop);
+ break; /* default */
+ }
+
+ pbuf_free(p);
+}
+
+
+/**
+ * Periodic timer for Neighbor discovery functions:
+ *
+ * - Update neighbor reachability states
+ * - Update destination cache entries age
+ * - Update invalidation timers of default routers and on-link prefixes
+ * - Perform duplicate address detection (DAD) for our addresses
+ * - Send router solicitations
+ */
+void
+nd6_tmr(void)
+{
+ s8_t i;
+ struct netif *netif;
+
+ /* Process neighbor entries. */
+ for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) {
+ switch (neighbor_cache[i].state) {
+ case ND6_INCOMPLETE:
+ if ((neighbor_cache[i].counter.probes_sent >= LWIP_ND6_MAX_MULTICAST_SOLICIT) &&
+ (!neighbor_cache[i].isrouter)) {
+ /* Retries exceeded. */
+ nd6_free_neighbor_cache_entry(i);
+ } else {
+ /* Send a NS for this entry. */
+ neighbor_cache[i].counter.probes_sent++;
+ nd6_send_neighbor_cache_probe(&neighbor_cache[i], ND6_SEND_FLAG_MULTICAST_DEST);
+ }
+ break;
+ case ND6_REACHABLE:
+ /* Send queued packets, if any are left. Should have been sent already. */
+ if (neighbor_cache[i].q != NULL) {
+ nd6_send_q(i);
+ }
+ if (neighbor_cache[i].counter.reachable_time <= ND6_TMR_INTERVAL) {
+ /* Change to stale state. */
+ neighbor_cache[i].state = ND6_STALE;
+ neighbor_cache[i].counter.stale_time = 0;
+ } else {
+ neighbor_cache[i].counter.reachable_time -= ND6_TMR_INTERVAL;
+ }
+ break;
+ case ND6_STALE:
+ neighbor_cache[i].counter.stale_time++;
+ break;
+ case ND6_DELAY:
+ if (neighbor_cache[i].counter.delay_time <= 1) {
+ /* Change to PROBE state. */
+ neighbor_cache[i].state = ND6_PROBE;
+ neighbor_cache[i].counter.probes_sent = 0;
+ } else {
+ neighbor_cache[i].counter.delay_time--;
+ }
+ break;
+ case ND6_PROBE:
+ if ((neighbor_cache[i].counter.probes_sent >= LWIP_ND6_MAX_MULTICAST_SOLICIT) &&
+ (!neighbor_cache[i].isrouter)) {
+ /* Retries exceeded. */
+ nd6_free_neighbor_cache_entry(i);
+ } else {
+ /* Send a NS for this entry. */
+ neighbor_cache[i].counter.probes_sent++;
+ nd6_send_neighbor_cache_probe(&neighbor_cache[i], 0);
+ }
+ break;
+ case ND6_NO_ENTRY:
+ default:
+ /* Do nothing. */
+ break;
+ }
+ }
+
+ /* Process destination entries. */
+ for (i = 0; i < LWIP_ND6_NUM_DESTINATIONS; i++) {
+ destination_cache[i].age++;
+ }
+
+ /* Process router entries. */
+ for (i = 0; i < LWIP_ND6_NUM_ROUTERS; i++) {
+ if (default_router_list[i].neighbor_entry != NULL) {
+ /* Active entry. */
+ if (default_router_list[i].invalidation_timer > 0) {
+ default_router_list[i].invalidation_timer -= ND6_TMR_INTERVAL / 1000;
+ }
+ if (default_router_list[i].invalidation_timer < ND6_TMR_INTERVAL / 1000) {
+ /* Less than 1 second remaining. Clear this entry. */
+ default_router_list[i].neighbor_entry->isrouter = 0;
+ default_router_list[i].neighbor_entry = NULL;
+ default_router_list[i].invalidation_timer = 0;
+ default_router_list[i].flags = 0;
+ }
+ }
+ }
+
+ /* Process prefix entries. */
+ for (i = 0; i < LWIP_ND6_NUM_PREFIXES; i++) {
+ if (prefix_list[i].netif != NULL) {
+ if (prefix_list[i].invalidation_timer < ND6_TMR_INTERVAL / 1000) {
+ /* Entry timed out, remove it */
+ prefix_list[i].invalidation_timer = 0;
+
+#if LWIP_IPV6_AUTOCONFIG
+ /* If any addresses were configured with this prefix, remove them */
+ if (prefix_list[i].flags & ND6_PREFIX_AUTOCONFIG_ADDRESS_GENERATED) {
+ s8_t j;
+
+ for (j = 1; j < LWIP_IPV6_NUM_ADDRESSES; j++) {
+ if ((netif_ip6_addr_state(prefix_list[i].netif, j) != IP6_ADDR_INVALID) &&
+ ip6_addr_netcmp(&prefix_list[i].prefix, netif_ip6_addr(prefix_list[i].netif, j))) {
+ netif_ip6_addr_set_state(prefix_list[i].netif, j, IP6_ADDR_INVALID);
+ prefix_list[i].flags = 0;
+
+ /* Exit loop. */
+ break;
+ }
+ }
+ }
+#endif /* LWIP_IPV6_AUTOCONFIG */
+
+ prefix_list[i].netif = NULL;
+ prefix_list[i].flags = 0;
+ } else {
+ prefix_list[i].invalidation_timer -= ND6_TMR_INTERVAL / 1000;
+
+#if LWIP_IPV6_AUTOCONFIG
+ /* Initiate address autoconfiguration for this prefix, if conditions are met. */
+ if (prefix_list[i].netif->ip6_autoconfig_enabled &&
+ (prefix_list[i].flags & ND6_PREFIX_AUTOCONFIG_AUTONOMOUS) &&
+ !(prefix_list[i].flags & ND6_PREFIX_AUTOCONFIG_ADDRESS_GENERATED)) {
+ s8_t j;
+ /* Try to get an address on this netif that is invalid.
+ * Skip 0 index (link-local address) */
+ for (j = 1; j < LWIP_IPV6_NUM_ADDRESSES; j++) {
+ if (netif_ip6_addr_state(prefix_list[i].netif, j) == IP6_ADDR_INVALID) {
+ /* Generate an address using this prefix and interface ID from link-local address. */
+ netif_ip6_addr_set_parts(prefix_list[i].netif, j,
+ prefix_list[i].prefix.addr[0], prefix_list[i].prefix.addr[1],
+ netif_ip6_addr(prefix_list[i].netif, 0)->addr[2], netif_ip6_addr(prefix_list[i].netif, 0)->addr[3]);
+
+ /* Mark it as tentative (DAD will be performed if configured). */
+ netif_ip6_addr_set_state(prefix_list[i].netif, j, IP6_ADDR_TENTATIVE);
+
+ /* Mark this prefix with ADDRESS_GENERATED, so that we don't try again. */
+ prefix_list[i].flags |= ND6_PREFIX_AUTOCONFIG_ADDRESS_GENERATED;
+
+ /* Exit loop. */
+ break;
+ }
+ }
+ }
+#endif /* LWIP_IPV6_AUTOCONFIG */
+ }
+ }
+ }
+
+
+ /* Process our own addresses, if DAD configured. */
+ for (netif = netif_list; netif != NULL; netif = netif->next) {
+ for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; ++i) {
+ u8_t addr_state = netif_ip6_addr_state(netif, i);
+ if (ip6_addr_istentative(addr_state)) {
+ if ((addr_state & IP6_ADDR_TENTATIVE_COUNT_MASK) >= LWIP_IPV6_DUP_DETECT_ATTEMPTS) {
+ /* No NA received in response. Mark address as valid. */
+ netif_ip6_addr_set_state(netif, i, IP6_ADDR_PREFERRED);
+ /* @todo implement preferred and valid lifetimes. */
+ } else if (netif->flags & NETIF_FLAG_UP) {
+ /* Send a NS for this address. */
+ nd6_send_ns(netif, netif_ip6_addr(netif, i), ND6_SEND_FLAG_MULTICAST_DEST);
+ /* tentative: set next state by increasing by one */
+ netif_ip6_addr_set_state(netif, i, addr_state + 1);
+ /* @todo send max 1 NS per tmr call? enable return*/
+ /*return;*/
+ }
+ }
+ }
+ }
+
+#if LWIP_IPV6_SEND_ROUTER_SOLICIT
+ /* Send router solicitation messages, if necessary. */
+ for (netif = netif_list; netif != NULL; netif = netif->next) {
+ if ((netif->rs_count > 0) && (netif->flags & NETIF_FLAG_UP) &&
+ (!ip6_addr_isinvalid(netif_ip6_addr_state(netif, 0)))) {
+ if (nd6_send_rs(netif) == ERR_OK) {
+ netif->rs_count--;
+ }
+ }
+ }
+#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */
+
+}
+
+/** Send a neighbor solicitation message for a specific neighbor cache entry
+ *
+ * @param entry the neightbor cache entry for wich to send the message
+ * @param flags one of ND6_SEND_FLAG_*
+ */
+static void
+nd6_send_neighbor_cache_probe(struct nd6_neighbor_cache_entry *entry, u8_t flags)
+{
+ nd6_send_ns(entry->netif, &entry->next_hop_address, flags);
+}
+
+/**
+ * Send a neighbor solicitation message
+ *
+ * @param netif the netif on which to send the message
+ * @param target_addr the IPv6 target address for the ND message
+ * @param flags one of ND6_SEND_FLAG_*
+ */
+static void
+nd6_send_ns(struct netif *netif, const ip6_addr_t *target_addr, u8_t flags)
+{
+ struct ns_header *ns_hdr;
+ struct pbuf *p;
+ const ip6_addr_t *src_addr;
+ u16_t lladdr_opt_len;
+
+ if (ip6_addr_isvalid(netif_ip6_addr_state(netif,0))) {
+ /* Use link-local address as source address. */
+ src_addr = netif_ip6_addr(netif, 0);
+ /* calculate option length (in 8-byte-blocks) */
+ lladdr_opt_len = ((netif->hwaddr_len + 2) + 7) >> 3;
+ } else {
+ src_addr = IP6_ADDR_ANY6;
+ /* Option "MUST NOT be included when the source IP address is the unspecified address." */
+ lladdr_opt_len = 0;
+ }
+
+ /* Allocate a packet. */
+ p = pbuf_alloc(PBUF_IP, sizeof(struct ns_header) + (lladdr_opt_len << 3), PBUF_RAM);
+ if (p == NULL) {
+ ND6_STATS_INC(nd6.memerr);
+ return;
+ }
+
+ /* Set fields. */
+ ns_hdr = (struct ns_header *)p->payload;
+
+ ns_hdr->type = ICMP6_TYPE_NS;
+ ns_hdr->code = 0;
+ ns_hdr->chksum = 0;
+ ns_hdr->reserved = 0;
+ ip6_addr_set(&(ns_hdr->target_address), target_addr);
+
+ if (lladdr_opt_len != 0) {
+ struct lladdr_option *lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct ns_header));
+ lladdr_opt->type = ND6_OPTION_TYPE_SOURCE_LLADDR;
+ lladdr_opt->length = (u8_t)lladdr_opt_len;
+ SMEMCPY(lladdr_opt->addr, netif->hwaddr, netif->hwaddr_len);
+ }
+
+ /* Generate the solicited node address for the target address. */
+ if (flags & ND6_SEND_FLAG_MULTICAST_DEST) {
+ ip6_addr_set_solicitednode(&multicast_address, target_addr->addr[3]);
+ target_addr = &multicast_address;
+ }
+
+#if CHECKSUM_GEN_ICMP6
+ IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_ICMP6) {
+ ns_hdr->chksum = ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->len, src_addr,
+ target_addr);
+ }
+#endif /* CHECKSUM_GEN_ICMP6 */
+
+ /* Send the packet out. */
+ ND6_STATS_INC(nd6.xmit);
+ ip6_output_if(p, (src_addr == IP6_ADDR_ANY6) ? NULL : src_addr, target_addr,
+ LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, netif);
+ pbuf_free(p);
+}
+
+/**
+ * Send a neighbor advertisement message
+ *
+ * @param netif the netif on which to send the message
+ * @param target_addr the IPv6 target address for the ND message
+ * @param flags one of ND6_SEND_FLAG_*
+ */
+static void
+nd6_send_na(struct netif *netif, const ip6_addr_t *target_addr, u8_t flags)
+{
+ struct na_header *na_hdr;
+ struct lladdr_option *lladdr_opt;
+ struct pbuf *p;
+ const ip6_addr_t *src_addr;
+ const ip6_addr_t *dest_addr;
+ u16_t lladdr_opt_len;
+
+ /* Use link-local address as source address. */
+ /* src_addr = netif_ip6_addr(netif, 0); */
+ /* Use target address as source address. */
+ src_addr = target_addr;
+
+ /* Allocate a packet. */
+ lladdr_opt_len = ((netif->hwaddr_len + 2) >> 3) + (((netif->hwaddr_len + 2) & 0x07) ? 1 : 0);
+ p = pbuf_alloc(PBUF_IP, sizeof(struct na_header) + (lladdr_opt_len << 3), PBUF_RAM);
+ if (p == NULL) {
+ ND6_STATS_INC(nd6.memerr);
+ return;
+ }
+
+ /* Set fields. */
+ na_hdr = (struct na_header *)p->payload;
+ lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct na_header));
+
+ na_hdr->type = ICMP6_TYPE_NA;
+ na_hdr->code = 0;
+ na_hdr->chksum = 0;
+ na_hdr->flags = flags & 0xf0;
+ na_hdr->reserved[0] = 0;
+ na_hdr->reserved[1] = 0;
+ na_hdr->reserved[2] = 0;
+ ip6_addr_set(&(na_hdr->target_address), target_addr);
+
+ lladdr_opt->type = ND6_OPTION_TYPE_TARGET_LLADDR;
+ lladdr_opt->length = (u8_t)lladdr_opt_len;
+ SMEMCPY(lladdr_opt->addr, netif->hwaddr, netif->hwaddr_len);
+
+ /* Generate the solicited node address for the target address. */
+ if (flags & ND6_SEND_FLAG_MULTICAST_DEST) {
+ ip6_addr_set_solicitednode(&multicast_address, target_addr->addr[3]);
+ dest_addr = &multicast_address;
+ } else if (flags & ND6_SEND_FLAG_ALLNODES_DEST) {
+ ip6_addr_set_allnodes_linklocal(&multicast_address);
+ dest_addr = &multicast_address;
+ } else {
+ dest_addr = ip6_current_src_addr();
+ }
+
+#if CHECKSUM_GEN_ICMP6
+ IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_ICMP6) {
+ na_hdr->chksum = ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->len, src_addr,
+ dest_addr);
+ }
+#endif /* CHECKSUM_GEN_ICMP6 */
+
+ /* Send the packet out. */
+ ND6_STATS_INC(nd6.xmit);
+ ip6_output_if(p, src_addr, dest_addr,
+ LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, netif);
+ pbuf_free(p);
+}
+
+#if LWIP_IPV6_SEND_ROUTER_SOLICIT
+/**
+ * Send a router solicitation message
+ *
+ * @param netif the netif on which to send the message
+ */
+static err_t
+nd6_send_rs(struct netif *netif)
+{
+ struct rs_header *rs_hdr;
+ struct lladdr_option *lladdr_opt;
+ struct pbuf *p;
+ const ip6_addr_t *src_addr;
+ err_t err;
+ u16_t lladdr_opt_len = 0;
+
+ /* Link-local source address, or unspecified address? */
+ if (ip6_addr_isvalid(netif_ip6_addr_state(netif, 0))) {
+ src_addr = netif_ip6_addr(netif, 0);
+ } else {
+ src_addr = IP6_ADDR_ANY6;
+ }
+
+ /* Generate the all routers target address. */
+ ip6_addr_set_allrouters_linklocal(&multicast_address);
+
+ /* Allocate a packet. */
+ if (src_addr != IP6_ADDR_ANY6) {
+ lladdr_opt_len = ((netif->hwaddr_len + 2) >> 3) + (((netif->hwaddr_len + 2) & 0x07) ? 1 : 0);
+ }
+ p = pbuf_alloc(PBUF_IP, sizeof(struct rs_header) + (lladdr_opt_len << 3), PBUF_RAM);
+ if (p == NULL) {
+ ND6_STATS_INC(nd6.memerr);
+ return ERR_BUF;
+ }
+
+ /* Set fields. */
+ rs_hdr = (struct rs_header *)p->payload;
+
+ rs_hdr->type = ICMP6_TYPE_RS;
+ rs_hdr->code = 0;
+ rs_hdr->chksum = 0;
+ rs_hdr->reserved = 0;
+
+ if (src_addr != IP6_ADDR_ANY6) {
+ /* Include our hw address. */
+ lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct rs_header));
+ lladdr_opt->type = ND6_OPTION_TYPE_SOURCE_LLADDR;
+ lladdr_opt->length = (u8_t)lladdr_opt_len;
+ SMEMCPY(lladdr_opt->addr, netif->hwaddr, netif->hwaddr_len);
+ }
+
+#if CHECKSUM_GEN_ICMP6
+ IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_ICMP6) {
+ rs_hdr->chksum = ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->len, src_addr,
+ &multicast_address);
+ }
+#endif /* CHECKSUM_GEN_ICMP6 */
+
+ /* Send the packet out. */
+ ND6_STATS_INC(nd6.xmit);
+
+ err = ip6_output_if(p, (src_addr == IP6_ADDR_ANY6) ? NULL : src_addr, &multicast_address,
+ LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, netif);
+ pbuf_free(p);
+
+ return err;
+}
+#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */
+
+/**
+ * Search for a neighbor cache entry
+ *
+ * @param ip6addr the IPv6 address of the neighbor
+ * @return The neighbor cache entry index that matched, -1 if no
+ * entry is found
+ */
+static s8_t
+nd6_find_neighbor_cache_entry(const ip6_addr_t *ip6addr)
+{
+ s8_t i;
+ for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) {
+ if (ip6_addr_cmp(ip6addr, &(neighbor_cache[i].next_hop_address))) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+/**
+ * Create a new neighbor cache entry.
+ *
+ * If no unused entry is found, will try to recycle an old entry
+ * according to ad-hoc "age" heuristic.
+ *
+ * @return The neighbor cache entry index that was created, -1 if no
+ * entry could be created
+ */
+static s8_t
+nd6_new_neighbor_cache_entry(void)
+{
+ s8_t i;
+ s8_t j;
+ u32_t time;
+
+
+ /* First, try to find an empty entry. */
+ for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) {
+ if (neighbor_cache[i].state == ND6_NO_ENTRY) {
+ return i;
+ }
+ }
+
+ /* We need to recycle an entry. in general, do not recycle if it is a router. */
+
+ /* Next, try to find a Stale entry. */
+ for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) {
+ if ((neighbor_cache[i].state == ND6_STALE) &&
+ (!neighbor_cache[i].isrouter)) {
+ nd6_free_neighbor_cache_entry(i);
+ return i;
+ }
+ }
+
+ /* Next, try to find a Probe entry. */
+ for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) {
+ if ((neighbor_cache[i].state == ND6_PROBE) &&
+ (!neighbor_cache[i].isrouter)) {
+ nd6_free_neighbor_cache_entry(i);
+ return i;
+ }
+ }
+
+ /* Next, try to find a Delayed entry. */
+ for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) {
+ if ((neighbor_cache[i].state == ND6_DELAY) &&
+ (!neighbor_cache[i].isrouter)) {
+ nd6_free_neighbor_cache_entry(i);
+ return i;
+ }
+ }
+
+ /* Next, try to find the oldest reachable entry. */
+ time = 0xfffffffful;
+ j = -1;
+ for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) {
+ if ((neighbor_cache[i].state == ND6_REACHABLE) &&
+ (!neighbor_cache[i].isrouter)) {
+ if (neighbor_cache[i].counter.reachable_time < time) {
+ j = i;
+ time = neighbor_cache[i].counter.reachable_time;
+ }
+ }
+ }
+ if (j >= 0) {
+ nd6_free_neighbor_cache_entry(j);
+ return j;
+ }
+
+ /* Next, find oldest incomplete entry without queued packets. */
+ time = 0;
+ j = -1;
+ for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) {
+ if (
+ (neighbor_cache[i].q == NULL) &&
+ (neighbor_cache[i].state == ND6_INCOMPLETE) &&
+ (!neighbor_cache[i].isrouter)) {
+ if (neighbor_cache[i].counter.probes_sent >= time) {
+ j = i;
+ time = neighbor_cache[i].counter.probes_sent;
+ }
+ }
+ }
+ if (j >= 0) {
+ nd6_free_neighbor_cache_entry(j);
+ return j;
+ }
+
+ /* Next, find oldest incomplete entry with queued packets. */
+ time = 0;
+ j = -1;
+ for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) {
+ if ((neighbor_cache[i].state == ND6_INCOMPLETE) &&
+ (!neighbor_cache[i].isrouter)) {
+ if (neighbor_cache[i].counter.probes_sent >= time) {
+ j = i;
+ time = neighbor_cache[i].counter.probes_sent;
+ }
+ }
+ }
+ if (j >= 0) {
+ nd6_free_neighbor_cache_entry(j);
+ return j;
+ }
+
+ /* No more entries to try. */
+ return -1;
+}
+
+/**
+ * Will free any resources associated with a neighbor cache
+ * entry, and will mark it as unused.
+ *
+ * @param i the neighbor cache entry index to free
+ */
+static void
+nd6_free_neighbor_cache_entry(s8_t i)
+{
+ if ((i < 0) || (i >= LWIP_ND6_NUM_NEIGHBORS)) {
+ return;
+ }
+ if (neighbor_cache[i].isrouter) {
+ /* isrouter needs to be cleared before deleting a neighbor cache entry */
+ return;
+ }
+
+ /* Free any queued packets. */
+ if (neighbor_cache[i].q != NULL) {
+ nd6_free_q(neighbor_cache[i].q);
+ neighbor_cache[i].q = NULL;
+ }
+
+ neighbor_cache[i].state = ND6_NO_ENTRY;
+ neighbor_cache[i].isrouter = 0;
+ neighbor_cache[i].netif = NULL;
+ neighbor_cache[i].counter.reachable_time = 0;
+ ip6_addr_set_zero(&(neighbor_cache[i].next_hop_address));
+}
+
+/**
+ * Search for a destination cache entry
+ *
+ * @param ip6addr the IPv6 address of the destination
+ * @return The destination cache entry index that matched, -1 if no
+ * entry is found
+ */
+static s8_t
+nd6_find_destination_cache_entry(const ip6_addr_t *ip6addr)
+{
+ s8_t i;
+ for (i = 0; i < LWIP_ND6_NUM_DESTINATIONS; i++) {
+ if (ip6_addr_cmp(ip6addr, &(destination_cache[i].destination_addr))) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+/**
+ * Create a new destination cache entry. If no unused entry is found,
+ * will recycle oldest entry.
+ *
+ * @return The destination cache entry index that was created, -1 if no
+ * entry was created
+ */
+static s8_t
+nd6_new_destination_cache_entry(void)
+{
+ s8_t i, j;
+ u32_t age;
+
+ /* Find an empty entry. */
+ for (i = 0; i < LWIP_ND6_NUM_DESTINATIONS; i++) {
+ if (ip6_addr_isany(&(destination_cache[i].destination_addr))) {
+ return i;
+ }
+ }
+
+ /* Find oldest entry. */
+ age = 0;
+ j = LWIP_ND6_NUM_DESTINATIONS - 1;
+ for (i = 0; i < LWIP_ND6_NUM_DESTINATIONS; i++) {
+ if (destination_cache[i].age > age) {
+ j = i;
+ }
+ }
+
+ return j;
+}
+
+/**
+ * Clear the destination cache.
+ *
+ * This operation may be necessary for consistency in the light of changing
+ * local addresses and/or use of the gateway hook.
+ */
+void
+nd6_clear_destination_cache(void)
+{
+ int i;
+
+ for (i = 0; i < LWIP_ND6_NUM_DESTINATIONS; i++) {
+ ip6_addr_set_any(&destination_cache[i].destination_addr);
+ }
+}
+
+/**
+ * Determine whether an address matches an on-link prefix.
+ *
+ * @param ip6addr the IPv6 address to match
+ * @return 1 if the address is on-link, 0 otherwise
+ */
+static s8_t
+nd6_is_prefix_in_netif(const ip6_addr_t *ip6addr, struct netif *netif)
+{
+ s8_t i;
+ for (i = 0; i < LWIP_ND6_NUM_PREFIXES; i++) {
+ if ((prefix_list[i].netif == netif) &&
+ (prefix_list[i].invalidation_timer > 0) &&
+ ip6_addr_netcmp(ip6addr, &(prefix_list[i].prefix))) {
+ return 1;
+ }
+ }
+ /* Check to see if address prefix matches a (manually?) configured address. */
+ for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+ if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) &&
+ ip6_addr_netcmp(ip6addr, netif_ip6_addr(netif, i))) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/**
+ * Select a default router for a destination.
+ *
+ * @param ip6addr the destination address
+ * @param netif the netif for the outgoing packet, if known
+ * @return the default router entry index, or -1 if no suitable
+ * router is found
+ */
+static s8_t
+nd6_select_router(const ip6_addr_t *ip6addr, struct netif *netif)
+{
+ s8_t i;
+ /* last_router is used for round-robin router selection (as recommended
+ * in RFC). This is more robust in case one router is not reachable,
+ * we are not stuck trying to resolve it. */
+ static s8_t last_router;
+ (void)ip6addr; /* @todo match preferred routes!! (must implement ND6_OPTION_TYPE_ROUTE_INFO) */
+
+ /* @todo: implement default router preference */
+
+ /* Look for reachable routers. */
+ for (i = 0; i < LWIP_ND6_NUM_ROUTERS; i++) {
+ if (++last_router >= LWIP_ND6_NUM_ROUTERS) {
+ last_router = 0;
+ }
+ if ((default_router_list[i].neighbor_entry != NULL) &&
+ (netif != NULL ? netif == default_router_list[i].neighbor_entry->netif : 1) &&
+ (default_router_list[i].invalidation_timer > 0) &&
+ (default_router_list[i].neighbor_entry->state == ND6_REACHABLE)) {
+ return i;
+ }
+ }
+
+ /* Look for router in other reachability states, but still valid according to timer. */
+ for (i = 0; i < LWIP_ND6_NUM_ROUTERS; i++) {
+ if (++last_router >= LWIP_ND6_NUM_ROUTERS) {
+ last_router = 0;
+ }
+ if ((default_router_list[i].neighbor_entry != NULL) &&
+ (netif != NULL ? netif == default_router_list[i].neighbor_entry->netif : 1) &&
+ (default_router_list[i].invalidation_timer > 0)) {
+ return i;
+ }
+ }
+
+ /* Look for any router for which we have any information at all. */
+ for (i = 0; i < LWIP_ND6_NUM_ROUTERS; i++) {
+ if (++last_router >= LWIP_ND6_NUM_ROUTERS) {
+ last_router = 0;
+ }
+ if (default_router_list[i].neighbor_entry != NULL &&
+ (netif != NULL ? netif == default_router_list[i].neighbor_entry->netif : 1)) {
+ return i;
+ }
+ }
+
+ /* no suitable router found. */
+ return -1;
+}
+
+/**
+ * Find a router-announced route to the given destination.
+ *
+ * The caller is responsible for checking whether the returned netif, if any,
+ * is in a suitable state (up, link up) to be used for packet transmission.
+ *
+ * @param ip6addr the destination IPv6 address
+ * @return the netif to use for the destination, or NULL if none found
+ */
+struct netif *
+nd6_find_route(const ip6_addr_t *ip6addr)
+{
+ s8_t i;
+
+ i = nd6_select_router(ip6addr, NULL);
+ if (i >= 0) {
+ if (default_router_list[i].neighbor_entry != NULL) {
+ return default_router_list[i].neighbor_entry->netif; /* may be NULL */
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * Find an entry for a default router.
+ *
+ * @param router_addr the IPv6 address of the router
+ * @param netif the netif on which the router is found, if known
+ * @return the index of the router entry, or -1 if not found
+ */
+static s8_t
+nd6_get_router(const ip6_addr_t *router_addr, struct netif *netif)
+{
+ s8_t i;
+
+ /* Look for router. */
+ for (i = 0; i < LWIP_ND6_NUM_ROUTERS; i++) {
+ if ((default_router_list[i].neighbor_entry != NULL) &&
+ ((netif != NULL) ? netif == default_router_list[i].neighbor_entry->netif : 1) &&
+ ip6_addr_cmp(router_addr, &(default_router_list[i].neighbor_entry->next_hop_address))) {
+ return i;
+ }
+ }
+
+ /* router not found. */
+ return -1;
+}
+
+/**
+ * Create a new entry for a default router.
+ *
+ * @param router_addr the IPv6 address of the router
+ * @param netif the netif on which the router is connected, if known
+ * @return the index on the router table, or -1 if could not be created
+ */
+static s8_t
+nd6_new_router(const ip6_addr_t *router_addr, struct netif *netif)
+{
+ s8_t router_index;
+ s8_t free_router_index;
+ s8_t neighbor_index;
+
+ /* Do we have a neighbor entry for this router? */
+ neighbor_index = nd6_find_neighbor_cache_entry(router_addr);
+ if (neighbor_index < 0) {
+ /* Create a neighbor entry for this router. */
+ neighbor_index = nd6_new_neighbor_cache_entry();
+ if (neighbor_index < 0) {
+ /* Could not create neighbor entry for this router. */
+ return -1;
+ }
+ ip6_addr_set(&(neighbor_cache[neighbor_index].next_hop_address), router_addr);
+ neighbor_cache[neighbor_index].netif = netif;
+ neighbor_cache[neighbor_index].q = NULL;
+ neighbor_cache[neighbor_index].state = ND6_INCOMPLETE;
+ neighbor_cache[neighbor_index].counter.probes_sent = 1;
+ nd6_send_neighbor_cache_probe(&neighbor_cache[neighbor_index], ND6_SEND_FLAG_MULTICAST_DEST);
+ }
+
+ /* Mark neighbor as router. */
+ neighbor_cache[neighbor_index].isrouter = 1;
+
+ /* Look for empty entry. */
+ free_router_index = LWIP_ND6_NUM_ROUTERS;
+ for (router_index = LWIP_ND6_NUM_ROUTERS - 1; router_index >= 0; router_index--) {
+ /* check if router already exists (this is a special case for 2 netifs on the same subnet
+ - e.g. wifi and cable) */
+ if(default_router_list[router_index].neighbor_entry == &(neighbor_cache[neighbor_index])){
+ return router_index;
+ }
+ if (default_router_list[router_index].neighbor_entry == NULL) {
+ /* remember lowest free index to create a new entry */
+ free_router_index = router_index;
+ }
+ }
+ if (free_router_index < LWIP_ND6_NUM_ROUTERS) {
+ default_router_list[free_router_index].neighbor_entry = &(neighbor_cache[neighbor_index]);
+ return free_router_index;
+ }
+
+ /* Could not create a router entry. */
+
+ /* Mark neighbor entry as not-router. Entry might be useful as neighbor still. */
+ neighbor_cache[neighbor_index].isrouter = 0;
+
+ /* router not found. */
+ return -1;
+}
+
+/**
+ * Find the cached entry for an on-link prefix.
+ *
+ * @param prefix the IPv6 prefix that is on-link
+ * @param netif the netif on which the prefix is on-link
+ * @return the index on the prefix table, or -1 if not found
+ */
+static s8_t
+nd6_get_onlink_prefix(ip6_addr_t *prefix, struct netif *netif)
+{
+ s8_t i;
+
+ /* Look for prefix in list. */
+ for (i = 0; i < LWIP_ND6_NUM_PREFIXES; ++i) {
+ if ((ip6_addr_netcmp(&(prefix_list[i].prefix), prefix)) &&
+ (prefix_list[i].netif == netif)) {
+ return i;
+ }
+ }
+
+ /* Entry not available. */
+ return -1;
+}
+
+/**
+ * Creates a new entry for an on-link prefix.
+ *
+ * @param prefix the IPv6 prefix that is on-link
+ * @param netif the netif on which the prefix is on-link
+ * @return the index on the prefix table, or -1 if not created
+ */
+static s8_t
+nd6_new_onlink_prefix(ip6_addr_t *prefix, struct netif *netif)
+{
+ s8_t i;
+
+ /* Create new entry. */
+ for (i = 0; i < LWIP_ND6_NUM_PREFIXES; ++i) {
+ if ((prefix_list[i].netif == NULL) ||
+ (prefix_list[i].invalidation_timer == 0)) {
+ /* Found empty prefix entry. */
+ prefix_list[i].netif = netif;
+ ip6_addr_set(&(prefix_list[i].prefix), prefix);
+#if LWIP_IPV6_AUTOCONFIG
+ prefix_list[i].flags = 0;
+#endif /* LWIP_IPV6_AUTOCONFIG */
+ return i;
+ }
+ }
+
+ /* Entry not available. */
+ return -1;
+}
+
+/**
+ * Determine the next hop for a destination. Will determine if the
+ * destination is on-link, else a suitable on-link router is selected.
+ *
+ * The last entry index is cached for fast entry search.
+ *
+ * @param ip6addr the destination address
+ * @param netif the netif on which the packet will be sent
+ * @return the neighbor cache entry for the next hop, ERR_RTE if no
+ * suitable next hop was found, ERR_MEM if no cache entry
+ * could be created
+ */
+static s8_t
+nd6_get_next_hop_entry(const ip6_addr_t *ip6addr, struct netif *netif)
+{
+#ifdef LWIP_HOOK_ND6_GET_GW
+ const ip6_addr_t *next_hop_addr;
+#endif /* LWIP_HOOK_ND6_GET_GW */
+ s8_t i;
+
+#if LWIP_NETIF_HWADDRHINT
+ if (netif->addr_hint != NULL) {
+ /* per-pcb cached entry was given */
+ u8_t addr_hint = *(netif->addr_hint);
+ if (addr_hint < LWIP_ND6_NUM_DESTINATIONS) {
+ nd6_cached_destination_index = addr_hint;
+ }
+ }
+#endif /* LWIP_NETIF_HWADDRHINT */
+
+ /* Look for ip6addr in destination cache. */
+ if (ip6_addr_cmp(ip6addr, &(destination_cache[nd6_cached_destination_index].destination_addr))) {
+ /* the cached entry index is the right one! */
+ /* do nothing. */
+ ND6_STATS_INC(nd6.cachehit);
+ } else {
+ /* Search destination cache. */
+ i = nd6_find_destination_cache_entry(ip6addr);
+ if (i >= 0) {
+ /* found destination entry. make it our new cached index. */
+ nd6_cached_destination_index = i;
+ } else {
+ /* Not found. Create a new destination entry. */
+ i = nd6_new_destination_cache_entry();
+ if (i >= 0) {
+ /* got new destination entry. make it our new cached index. */
+ nd6_cached_destination_index = i;
+ } else {
+ /* Could not create a destination cache entry. */
+ return ERR_MEM;
+ }
+
+ /* Copy dest address to destination cache. */
+ ip6_addr_set(&(destination_cache[nd6_cached_destination_index].destination_addr), ip6addr);
+
+ /* Now find the next hop. is it a neighbor? */
+ if (ip6_addr_islinklocal(ip6addr) ||
+ nd6_is_prefix_in_netif(ip6addr, netif)) {
+ /* Destination in local link. */
+ destination_cache[nd6_cached_destination_index].pmtu = netif->mtu;
+ ip6_addr_copy(destination_cache[nd6_cached_destination_index].next_hop_addr, destination_cache[nd6_cached_destination_index].destination_addr);
+#ifdef LWIP_HOOK_ND6_GET_GW
+ } else if ((next_hop_addr = LWIP_HOOK_ND6_GET_GW(netif, ip6addr)) != NULL) {
+ /* Next hop for destination provided by hook function. */
+ destination_cache[nd6_cached_destination_index].pmtu = netif->mtu;
+ ip6_addr_set(&destination_cache[nd6_cached_destination_index].next_hop_addr, next_hop_addr);
+#endif /* LWIP_HOOK_ND6_GET_GW */
+ } else {
+ /* We need to select a router. */
+ i = nd6_select_router(ip6addr, netif);
+ if (i < 0) {
+ /* No router found. */
+ ip6_addr_set_any(&(destination_cache[nd6_cached_destination_index].destination_addr));
+ return ERR_RTE;
+ }
+ destination_cache[nd6_cached_destination_index].pmtu = netif->mtu; /* Start with netif mtu, correct through ICMPv6 if necessary */
+ ip6_addr_copy(destination_cache[nd6_cached_destination_index].next_hop_addr, default_router_list[i].neighbor_entry->next_hop_address);
+ }
+ }
+ }
+
+#if LWIP_NETIF_HWADDRHINT
+ if (netif->addr_hint != NULL) {
+ /* per-pcb cached entry was given */
+ *(netif->addr_hint) = nd6_cached_destination_index;
+ }
+#endif /* LWIP_NETIF_HWADDRHINT */
+
+ /* Look in neighbor cache for the next-hop address. */
+ if (ip6_addr_cmp(&(destination_cache[nd6_cached_destination_index].next_hop_addr),
+ &(neighbor_cache[nd6_cached_neighbor_index].next_hop_address))) {
+ /* Cache hit. */
+ /* Do nothing. */
+ ND6_STATS_INC(nd6.cachehit);
+ } else {
+ i = nd6_find_neighbor_cache_entry(&(destination_cache[nd6_cached_destination_index].next_hop_addr));
+ if (i >= 0) {
+ /* Found a matching record, make it new cached entry. */
+ nd6_cached_neighbor_index = i;
+ } else {
+ /* Neighbor not in cache. Make a new entry. */
+ i = nd6_new_neighbor_cache_entry();
+ if (i >= 0) {
+ /* got new neighbor entry. make it our new cached index. */
+ nd6_cached_neighbor_index = i;
+ } else {
+ /* Could not create a neighbor cache entry. */
+ return ERR_MEM;
+ }
+
+ /* Initialize fields. */
+ ip6_addr_copy(neighbor_cache[i].next_hop_address,
+ destination_cache[nd6_cached_destination_index].next_hop_addr);
+ neighbor_cache[i].isrouter = 0;
+ neighbor_cache[i].netif = netif;
+ neighbor_cache[i].state = ND6_INCOMPLETE;
+ neighbor_cache[i].counter.probes_sent = 1;
+ nd6_send_neighbor_cache_probe(&neighbor_cache[i], ND6_SEND_FLAG_MULTICAST_DEST);
+ }
+ }
+
+ /* Reset this destination's age. */
+ destination_cache[nd6_cached_destination_index].age = 0;
+
+ return nd6_cached_neighbor_index;
+}
+
+/**
+ * Queue a packet for a neighbor.
+ *
+ * @param neighbor_index the index in the neighbor cache table
+ * @param q packet to be queued
+ * @return ERR_OK if succeeded, ERR_MEM if out of memory
+ */
+static err_t
+nd6_queue_packet(s8_t neighbor_index, struct pbuf *q)
+{
+ err_t result = ERR_MEM;
+ struct pbuf *p;
+ int copy_needed = 0;
+#if LWIP_ND6_QUEUEING
+ struct nd6_q_entry *new_entry, *r;
+#endif /* LWIP_ND6_QUEUEING */
+
+ if ((neighbor_index < 0) || (neighbor_index >= LWIP_ND6_NUM_NEIGHBORS)) {
+ return ERR_ARG;
+ }
+
+ /* IF q includes a PBUF_REF, PBUF_POOL or PBUF_RAM, we have no choice but
+ * to copy the whole queue into a new PBUF_RAM (see bug #11400)
+ * PBUF_ROMs can be left as they are, since ROM must not get changed. */
+ p = q;
+ while (p) {
+ if (p->type != PBUF_ROM) {
+ copy_needed = 1;
+ break;
+ }
+ p = p->next;
+ }
+ if (copy_needed) {
+ /* copy the whole packet into new pbufs */
+ p = pbuf_alloc(PBUF_LINK, q->tot_len, PBUF_RAM);
+ while ((p == NULL) && (neighbor_cache[neighbor_index].q != NULL)) {
+ /* Free oldest packet (as per RFC recommendation) */
+#if LWIP_ND6_QUEUEING
+ r = neighbor_cache[neighbor_index].q;
+ neighbor_cache[neighbor_index].q = r->next;
+ r->next = NULL;
+ nd6_free_q(r);
+#else /* LWIP_ND6_QUEUEING */
+ pbuf_free(neighbor_cache[neighbor_index].q);
+ neighbor_cache[neighbor_index].q = NULL;
+#endif /* LWIP_ND6_QUEUEING */
+ p = pbuf_alloc(PBUF_LINK, q->tot_len, PBUF_RAM);
+ }
+ if (p != NULL) {
+ if (pbuf_copy(p, q) != ERR_OK) {
+ pbuf_free(p);
+ p = NULL;
+ }
+ }
+ } else {
+ /* referencing the old pbuf is enough */
+ p = q;
+ pbuf_ref(p);
+ }
+ /* packet was copied/ref'd? */
+ if (p != NULL) {
+ /* queue packet ... */
+#if LWIP_ND6_QUEUEING
+ /* allocate a new nd6 queue entry */
+ new_entry = (struct nd6_q_entry *)memp_malloc(MEMP_ND6_QUEUE);
+ if ((new_entry == NULL) && (neighbor_cache[neighbor_index].q != NULL)) {
+ /* Free oldest packet (as per RFC recommendation) */
+ r = neighbor_cache[neighbor_index].q;
+ neighbor_cache[neighbor_index].q = r->next;
+ r->next = NULL;
+ nd6_free_q(r);
+ new_entry = (struct nd6_q_entry *)memp_malloc(MEMP_ND6_QUEUE);
+ }
+ if (new_entry != NULL) {
+ new_entry->next = NULL;
+ new_entry->p = p;
+ if (neighbor_cache[neighbor_index].q != NULL) {
+ /* queue was already existent, append the new entry to the end */
+ r = neighbor_cache[neighbor_index].q;
+ while (r->next != NULL) {
+ r = r->next;
+ }
+ r->next = new_entry;
+ } else {
+ /* queue did not exist, first item in queue */
+ neighbor_cache[neighbor_index].q = new_entry;
+ }
+ LWIP_DEBUGF(LWIP_DBG_TRACE, ("ipv6: queued packet %p on neighbor entry %"S16_F"\n", (void *)p, (s16_t)neighbor_index));
+ result = ERR_OK;
+ } else {
+ /* the pool MEMP_ND6_QUEUE is empty */
+ pbuf_free(p);
+ LWIP_DEBUGF(LWIP_DBG_TRACE, ("ipv6: could not queue a copy of packet %p (out of memory)\n", (void *)p));
+ /* { result == ERR_MEM } through initialization */
+ }
+#else /* LWIP_ND6_QUEUEING */
+ /* Queue a single packet. If an older packet is already queued, free it as per RFC. */
+ if (neighbor_cache[neighbor_index].q != NULL) {
+ pbuf_free(neighbor_cache[neighbor_index].q);
+ }
+ neighbor_cache[neighbor_index].q = p;
+ LWIP_DEBUGF(LWIP_DBG_TRACE, ("ipv6: queued packet %p on neighbor entry %"S16_F"\n", (void *)p, (s16_t)neighbor_index));
+ result = ERR_OK;
+#endif /* LWIP_ND6_QUEUEING */
+ } else {
+ LWIP_DEBUGF(LWIP_DBG_TRACE, ("ipv6: could not queue a copy of packet %p (out of memory)\n", (void *)q));
+ /* { result == ERR_MEM } through initialization */
+ }
+
+ return result;
+}
+
+#if LWIP_ND6_QUEUEING
+/**
+ * Free a complete queue of nd6 q entries
+ *
+ * @param q a queue of nd6_q_entry to free
+ */
+static void
+nd6_free_q(struct nd6_q_entry *q)
+{
+ struct nd6_q_entry *r;
+ LWIP_ASSERT("q != NULL", q != NULL);
+ LWIP_ASSERT("q->p != NULL", q->p != NULL);
+ while (q) {
+ r = q;
+ q = q->next;
+ LWIP_ASSERT("r->p != NULL", (r->p != NULL));
+ pbuf_free(r->p);
+ memp_free(MEMP_ND6_QUEUE, r);
+ }
+}
+#endif /* LWIP_ND6_QUEUEING */
+
+/**
+ * Send queued packets for a neighbor
+ *
+ * @param i the neighbor to send packets to
+ */
+static void
+nd6_send_q(s8_t i)
+{
+ struct ip6_hdr *ip6hdr;
+ ip6_addr_t dest;
+#if LWIP_ND6_QUEUEING
+ struct nd6_q_entry *q;
+#endif /* LWIP_ND6_QUEUEING */
+
+ if ((i < 0) || (i >= LWIP_ND6_NUM_NEIGHBORS)) {
+ return;
+ }
+
+#if LWIP_ND6_QUEUEING
+ while (neighbor_cache[i].q != NULL) {
+ /* remember first in queue */
+ q = neighbor_cache[i].q;
+ /* pop first item off the queue */
+ neighbor_cache[i].q = q->next;
+ /* Get ipv6 header. */
+ ip6hdr = (struct ip6_hdr *)(q->p->payload);
+ /* Create an aligned copy. */
+ ip6_addr_set(&dest, &(ip6hdr->dest));
+ /* send the queued IPv6 packet */
+ (neighbor_cache[i].netif)->output_ip6(neighbor_cache[i].netif, q->p, &dest);
+ /* free the queued IP packet */
+ pbuf_free(q->p);
+ /* now queue entry can be freed */
+ memp_free(MEMP_ND6_QUEUE, q);
+ }
+#else /* LWIP_ND6_QUEUEING */
+ if (neighbor_cache[i].q != NULL) {
+ /* Get ipv6 header. */
+ ip6hdr = (struct ip6_hdr *)(neighbor_cache[i].q->payload);
+ /* Create an aligned copy. */
+ ip6_addr_set(&dest, &(ip6hdr->dest));
+ /* send the queued IPv6 packet */
+ (neighbor_cache[i].netif)->output_ip6(neighbor_cache[i].netif, neighbor_cache[i].q, &dest);
+ /* free the queued IP packet */
+ pbuf_free(neighbor_cache[i].q);
+ neighbor_cache[i].q = NULL;
+ }
+#endif /* LWIP_ND6_QUEUEING */
+}
+
+/**
+ * A packet is to be transmitted to a specific IPv6 destination on a specific
+ * interface. Check if we can find the hardware address of the next hop to use
+ * for the packet. If so, give the hardware address to the caller, which should
+ * use it to send the packet right away. Otherwise, enqueue the packet for
+ * later transmission while looking up the hardware address, if possible.
+ *
+ * As such, this function returns one of three different possible results:
+ *
+ * - ERR_OK with a non-NULL 'hwaddrp': the caller should send the packet now.
+ * - ERR_OK with a NULL 'hwaddrp': the packet has been enqueued for later.
+ * - not ERR_OK: something went wrong; forward the error upward in the stack.
+ *
+ * @param netif The lwIP network interface on which the IP packet will be sent.
+ * @param q The pbuf(s) containing the IP packet to be sent.
+ * @param ip6addr The destination IPv6 address of the packet.
+ * @param hwaddrp On success, filled with a pointer to a HW address or NULL (meaning
+ * the packet has been queued).
+ * @return
+ * - ERR_OK on success, ERR_RTE if no route was found for the packet,
+ * or ERR_MEM if low memory conditions prohibit sending the packet at all.
+ */
+err_t
+nd6_get_next_hop_addr_or_queue(struct netif *netif, struct pbuf *q, const ip6_addr_t *ip6addr, const u8_t **hwaddrp)
+{
+ s8_t i;
+
+ /* Get next hop record. */
+ i = nd6_get_next_hop_entry(ip6addr, netif);
+ if (i < 0) {
+ /* failed to get a next hop neighbor record. */
+ return i;
+ }
+
+ /* Now that we have a destination record, send or queue the packet. */
+ if (neighbor_cache[i].state == ND6_STALE) {
+ /* Switch to delay state. */
+ neighbor_cache[i].state = ND6_DELAY;
+ neighbor_cache[i].counter.delay_time = LWIP_ND6_DELAY_FIRST_PROBE_TIME / ND6_TMR_INTERVAL;
+ }
+ /* @todo should we send or queue if PROBE? send for now, to let unicast NS pass. */
+ if ((neighbor_cache[i].state == ND6_REACHABLE) ||
+ (neighbor_cache[i].state == ND6_DELAY) ||
+ (neighbor_cache[i].state == ND6_PROBE)) {
+
+ /* Tell the caller to send out the packet now. */
+ *hwaddrp = neighbor_cache[i].lladdr;
+ return ERR_OK;
+ }
+
+ /* We should queue packet on this interface. */
+ *hwaddrp = NULL;
+ return nd6_queue_packet(i, q);
+}
+
+
+/**
+ * Get the Path MTU for a destination.
+ *
+ * @param ip6addr the destination address
+ * @param netif the netif on which the packet will be sent
+ * @return the Path MTU, if known, or the netif default MTU
+ */
+u16_t
+nd6_get_destination_mtu(const ip6_addr_t *ip6addr, struct netif *netif)
+{
+ s8_t i;
+
+ i = nd6_find_destination_cache_entry(ip6addr);
+ if (i >= 0) {
+ if (destination_cache[i].pmtu > 0) {
+ return destination_cache[i].pmtu;
+ }
+ }
+
+ if (netif != NULL) {
+ return netif->mtu;
+ }
+
+ return 1280; /* Minimum MTU */
+}
+
+
+#if LWIP_ND6_TCP_REACHABILITY_HINTS
+/**
+ * Provide the Neighbor discovery process with a hint that a
+ * destination is reachable. Called by tcp_receive when ACKs are
+ * received or sent (as per RFC). This is useful to avoid sending
+ * NS messages every 30 seconds.
+ *
+ * @param ip6addr the destination address which is know to be reachable
+ * by an upper layer protocol (TCP)
+ */
+void
+nd6_reachability_hint(const ip6_addr_t *ip6addr)
+{
+ s8_t i;
+
+ /* Find destination in cache. */
+ if (ip6_addr_cmp(ip6addr, &(destination_cache[nd6_cached_destination_index].destination_addr))) {
+ i = nd6_cached_destination_index;
+ ND6_STATS_INC(nd6.cachehit);
+ } else {
+ i = nd6_find_destination_cache_entry(ip6addr);
+ }
+ if (i < 0) {
+ return;
+ }
+
+ /* Find next hop neighbor in cache. */
+ if (ip6_addr_cmp(&(destination_cache[i].next_hop_addr), &(neighbor_cache[nd6_cached_neighbor_index].next_hop_address))) {
+ i = nd6_cached_neighbor_index;
+ ND6_STATS_INC(nd6.cachehit);
+ } else {
+ i = nd6_find_neighbor_cache_entry(&(destination_cache[i].next_hop_addr));
+ }
+ if (i < 0) {
+ return;
+ }
+
+ /* For safety: don't set as reachable if we don't have a LL address yet. Misuse protection. */
+ if (neighbor_cache[i].state == ND6_INCOMPLETE || neighbor_cache[i].state == ND6_NO_ENTRY) {
+ return;
+ }
+
+ /* Set reachability state. */
+ neighbor_cache[i].state = ND6_REACHABLE;
+ neighbor_cache[i].counter.reachable_time = reachable_time;
+}
+#endif /* LWIP_ND6_TCP_REACHABILITY_HINTS */
+
+/**
+ * Remove all prefix, neighbor_cache and router entries of the specified netif.
+ *
+ * @param netif points to a network interface
+ */
+void
+nd6_cleanup_netif(struct netif *netif)
+{
+ u8_t i;
+ s8_t router_index;
+ for (i = 0; i < LWIP_ND6_NUM_PREFIXES; i++) {
+ if (prefix_list[i].netif == netif) {
+ prefix_list[i].netif = NULL;
+ prefix_list[i].flags = 0;
+ }
+ }
+ for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) {
+ if (neighbor_cache[i].netif == netif) {
+ for (router_index = 0; router_index < LWIP_ND6_NUM_ROUTERS; router_index++) {
+ if (default_router_list[router_index].neighbor_entry == &neighbor_cache[i]) {
+ default_router_list[router_index].neighbor_entry = NULL;
+ default_router_list[router_index].flags = 0;
+ }
+ }
+ neighbor_cache[i].isrouter = 0;
+ nd6_free_neighbor_cache_entry(i);
+ }
+ }
+}
+
+#if LWIP_IPV6_MLD
+/**
+ * The state of a local IPv6 address entry is about to change. If needed, join
+ * or leave the solicited-node multicast group for the address.
+ *
+ * @param netif The netif that owns the address.
+ * @param addr_idx The index of the address.
+ * @param new_state The new (IP6_ADDR_) state for the address.
+ */
+void
+nd6_adjust_mld_membership(struct netif *netif, s8_t addr_idx, u8_t new_state)
+{
+ u8_t old_state, old_member, new_member;
+
+ old_state = netif_ip6_addr_state(netif, addr_idx);
+
+ /* Determine whether we were, and should be, a member of the solicited-node
+ * multicast group for this address. For tentative addresses, the group is
+ * not joined until the address enters the TENTATIVE_1 (or VALID) state. */
+ old_member = (old_state != IP6_ADDR_INVALID && old_state != IP6_ADDR_TENTATIVE);
+ new_member = (new_state != IP6_ADDR_INVALID && new_state != IP6_ADDR_TENTATIVE);
+
+ if (old_member != new_member) {
+ ip6_addr_set_solicitednode(&multicast_address, netif_ip6_addr(netif, addr_idx)->addr[3]);
+
+ if (new_member) {
+ mld6_joingroup_netif(netif, &multicast_address);
+ } else {
+ mld6_leavegroup_netif(netif, &multicast_address);
+ }
+ }
+}
+#endif /* LWIP_IPV6_MLD */
+
+#endif /* LWIP_IPV6 */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/mem.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/mem.c
new file mode 100644
index 0000000..db3b7cc
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/mem.c
@@ -0,0 +1,777 @@
+/**
+ * @file
+ * Dynamic memory manager
+ *
+ * This is a lightweight replacement for the standard C library malloc().
+ *
+ * If you want to use the standard C library malloc() instead, define
+ * MEM_LIBC_MALLOC to 1 in your lwipopts.h
+ *
+ * To let mem_malloc() use pools (prevents fragmentation and is much faster than
+ * a heap but might waste some memory), define MEM_USE_POOLS to 1, define
+ * MEMP_USE_CUSTOM_POOLS to 1 and create a file "lwippools.h" that includes a list
+ * of pools like this (more pools can be added between _START and _END):
+ *
+ * Define three pools with sizes 256, 512, and 1512 bytes
+ * LWIP_MALLOC_MEMPOOL_START
+ * LWIP_MALLOC_MEMPOOL(20, 256)
+ * LWIP_MALLOC_MEMPOOL(10, 512)
+ * LWIP_MALLOC_MEMPOOL(5, 1512)
+ * LWIP_MALLOC_MEMPOOL_END
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ * Simon Goldschmidt
+ *
+ */
+
+#include "lwip/opt.h"
+#include "lwip/mem.h"
+#include "lwip/def.h"
+#include "lwip/sys.h"
+#include "lwip/stats.h"
+#include "lwip/err.h"
+
+#include <string.h>
+
+#if MEM_LIBC_MALLOC
+#include <stdlib.h> /* for malloc()/free() */
+#endif
+
+#if MEM_LIBC_MALLOC || MEM_USE_POOLS
+
+/** mem_init is not used when using pools instead of a heap or using
+ * C library malloc().
+ */
+void
+mem_init(void)
+{
+}
+
+/** mem_trim is not used when using pools instead of a heap or using
+ * C library malloc(): we can't free part of a pool element and the stack
+ * support mem_trim() to return a different pointer
+ */
+void*
+mem_trim(void *mem, mem_size_t size)
+{
+ LWIP_UNUSED_ARG(size);
+ return mem;
+}
+#endif /* MEM_LIBC_MALLOC || MEM_USE_POOLS */
+
+#if MEM_LIBC_MALLOC
+/* lwIP heap implemented using C library malloc() */
+
+/* in case C library malloc() needs extra protection,
+ * allow these defines to be overridden.
+ */
+#ifndef mem_clib_free
+#define mem_clib_free free
+#endif
+#ifndef mem_clib_malloc
+#define mem_clib_malloc malloc
+#endif
+#ifndef mem_clib_calloc
+#define mem_clib_calloc calloc
+#endif
+
+#if LWIP_STATS && MEM_STATS
+#define MEM_LIBC_STATSHELPER_SIZE LWIP_MEM_ALIGN_SIZE(sizeof(mem_size_t))
+#else
+#define MEM_LIBC_STATSHELPER_SIZE 0
+#endif
+
+/**
+ * Allocate a block of memory with a minimum of 'size' bytes.
+ *
+ * @param size is the minimum size of the requested block in bytes.
+ * @return pointer to allocated memory or NULL if no free memory was found.
+ *
+ * Note that the returned value must always be aligned (as defined by MEM_ALIGNMENT).
+ */
+void *
+mem_malloc(mem_size_t size)
+{
+ void* ret = mem_clib_malloc(size + MEM_LIBC_STATSHELPER_SIZE);
+ if (ret == NULL) {
+ MEM_STATS_INC(err);
+ } else {
+ LWIP_ASSERT("malloc() must return aligned memory", LWIP_MEM_ALIGN(ret) == ret);
+#if LWIP_STATS && MEM_STATS
+ *(mem_size_t*)ret = size;
+ ret = (u8_t*)ret + MEM_LIBC_STATSHELPER_SIZE;
+ MEM_STATS_INC_USED(used, size);
+#endif
+ }
+ return ret;
+}
+
+/** Put memory back on the heap
+ *
+ * @param rmem is the pointer as returned by a previous call to mem_malloc()
+ */
+void
+mem_free(void *rmem)
+{
+ LWIP_ASSERT("rmem != NULL", (rmem != NULL));
+ LWIP_ASSERT("rmem == MEM_ALIGN(rmem)", (rmem == LWIP_MEM_ALIGN(rmem)));
+#if LWIP_STATS && MEM_STATS
+ rmem = (u8_t*)rmem - MEM_LIBC_STATSHELPER_SIZE;
+ MEM_STATS_DEC_USED(used, *(mem_size_t*)rmem);
+#endif
+ mem_clib_free(rmem);
+}
+
+#elif MEM_USE_POOLS
+
+/* lwIP heap implemented with different sized pools */
+
+/**
+ * Allocate memory: determine the smallest pool that is big enough
+ * to contain an element of 'size' and get an element from that pool.
+ *
+ * @param size the size in bytes of the memory needed
+ * @return a pointer to the allocated memory or NULL if the pool is empty
+ */
+void *
+mem_malloc(mem_size_t size)
+{
+ void *ret;
+ struct memp_malloc_helper *element = NULL;
+ memp_t poolnr;
+ mem_size_t required_size = size + LWIP_MEM_ALIGN_SIZE(sizeof(struct memp_malloc_helper));
+
+ for (poolnr = MEMP_POOL_FIRST; poolnr <= MEMP_POOL_LAST; poolnr = (memp_t)(poolnr + 1)) {
+ /* is this pool big enough to hold an element of the required size
+ plus a struct memp_malloc_helper that saves the pool this element came from? */
+ if (required_size <= memp_pools[poolnr]->size) {
+ element = (struct memp_malloc_helper*)memp_malloc(poolnr);
+ if (element == NULL) {
+ /* No need to DEBUGF or ASSERT: This error is already taken care of in memp.c */
+#if MEM_USE_POOLS_TRY_BIGGER_POOL
+ /** Try a bigger pool if this one is empty! */
+ if (poolnr < MEMP_POOL_LAST) {
+ continue;
+ }
+#endif /* MEM_USE_POOLS_TRY_BIGGER_POOL */
+ MEM_STATS_INC(err);
+ return NULL;
+ }
+ break;
+ }
+ }
+ if (poolnr > MEMP_POOL_LAST) {
+ LWIP_ASSERT("mem_malloc(): no pool is that big!", 0);
+ MEM_STATS_INC(err);
+ return NULL;
+ }
+
+ /* save the pool number this element came from */
+ element->poolnr = poolnr;
+ /* and return a pointer to the memory directly after the struct memp_malloc_helper */
+ ret = (u8_t*)element + LWIP_MEM_ALIGN_SIZE(sizeof(struct memp_malloc_helper));
+
+#if MEMP_OVERFLOW_CHECK || (LWIP_STATS && MEM_STATS)
+ /* truncating to u16_t is safe because struct memp_desc::size is u16_t */
+ element->size = (u16_t)size;
+ MEM_STATS_INC_USED(used, element->size);
+#endif /* MEMP_OVERFLOW_CHECK || (LWIP_STATS && MEM_STATS) */
+#if MEMP_OVERFLOW_CHECK
+ /* initialize unused memory (diff between requested size and selected pool's size) */
+ memset((u8_t*)ret + size, 0xcd, memp_pools[poolnr]->size - size);
+#endif /* MEMP_OVERFLOW_CHECK */
+ return ret;
+}
+
+/**
+ * Free memory previously allocated by mem_malloc. Loads the pool number
+ * and calls memp_free with that pool number to put the element back into
+ * its pool
+ *
+ * @param rmem the memory element to free
+ */
+void
+mem_free(void *rmem)
+{
+ struct memp_malloc_helper *hmem;
+
+ LWIP_ASSERT("rmem != NULL", (rmem != NULL));
+ LWIP_ASSERT("rmem == MEM_ALIGN(rmem)", (rmem == LWIP_MEM_ALIGN(rmem)));
+
+ /* get the original struct memp_malloc_helper */
+ /* cast through void* to get rid of alignment warnings */
+ hmem = (struct memp_malloc_helper*)(void*)((u8_t*)rmem - LWIP_MEM_ALIGN_SIZE(sizeof(struct memp_malloc_helper)));
+
+ LWIP_ASSERT("hmem != NULL", (hmem != NULL));
+ LWIP_ASSERT("hmem == MEM_ALIGN(hmem)", (hmem == LWIP_MEM_ALIGN(hmem)));
+ LWIP_ASSERT("hmem->poolnr < MEMP_MAX", (hmem->poolnr < MEMP_MAX));
+
+ MEM_STATS_DEC_USED(used, hmem->size);
+#if MEMP_OVERFLOW_CHECK
+ {
+ u16_t i;
+ LWIP_ASSERT("MEM_USE_POOLS: invalid chunk size",
+ hmem->size <= memp_pools[hmem->poolnr]->size);
+ /* check that unused memory remained untouched (diff between requested size and selected pool's size) */
+ for (i = hmem->size; i < memp_pools[hmem->poolnr]->size; i++) {
+ u8_t data = *((u8_t*)rmem + i);
+ LWIP_ASSERT("MEM_USE_POOLS: mem overflow detected", data == 0xcd);
+ }
+ }
+#endif /* MEMP_OVERFLOW_CHECK */
+
+ /* and put it in the pool we saved earlier */
+ memp_free(hmem->poolnr, hmem);
+}
+
+#else /* MEM_USE_POOLS */
+/* lwIP replacement for your libc malloc() */
+
+/**
+ * The heap is made up as a list of structs of this type.
+ * This does not have to be aligned since for getting its size,
+ * we only use the macro SIZEOF_STRUCT_MEM, which automatically aligns.
+ */
+struct mem {
+ /** index (-> ram[next]) of the next struct */
+ mem_size_t next;
+ /** index (-> ram[prev]) of the previous struct */
+ mem_size_t prev;
+ /** 1: this area is used; 0: this area is unused */
+ u8_t used;
+};
+
+/** All allocated blocks will be MIN_SIZE bytes big, at least!
+ * MIN_SIZE can be overridden to suit your needs. Smaller values save space,
+ * larger values could prevent too small blocks to fragment the RAM too much. */
+#ifndef MIN_SIZE
+#define MIN_SIZE 12
+#endif /* MIN_SIZE */
+/* some alignment macros: we define them here for better source code layout */
+#define MIN_SIZE_ALIGNED LWIP_MEM_ALIGN_SIZE(MIN_SIZE)
+#define SIZEOF_STRUCT_MEM LWIP_MEM_ALIGN_SIZE(sizeof(struct mem))
+#define MEM_SIZE_ALIGNED LWIP_MEM_ALIGN_SIZE(MEM_SIZE)
+
+/** If you want to relocate the heap to external memory, simply define
+ * LWIP_RAM_HEAP_POINTER as a void-pointer to that location.
+ * If so, make sure the memory at that location is big enough (see below on
+ * how that space is calculated). */
+#ifndef LWIP_RAM_HEAP_POINTER
+/** the heap. we need one struct mem at the end and some room for alignment */
+LWIP_DECLARE_MEMORY_ALIGNED(ram_heap, MEM_SIZE_ALIGNED + (2U*SIZEOF_STRUCT_MEM));
+#define LWIP_RAM_HEAP_POINTER ram_heap
+#endif /* LWIP_RAM_HEAP_POINTER */
+
+/** pointer to the heap (ram_heap): for alignment, ram is now a pointer instead of an array */
+static u8_t *ram;
+/** the last entry, always unused! */
+static struct mem *ram_end;
+/** pointer to the lowest free block, this is used for faster search */
+static struct mem *lfree;
+
+/** concurrent access protection */
+#if !NO_SYS
+static sys_mutex_t mem_mutex;
+#endif
+
+#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
+
+static volatile u8_t mem_free_count;
+
+/* Allow mem_free from other (e.g. interrupt) context */
+#define LWIP_MEM_FREE_DECL_PROTECT() SYS_ARCH_DECL_PROTECT(lev_free)
+#define LWIP_MEM_FREE_PROTECT() SYS_ARCH_PROTECT(lev_free)
+#define LWIP_MEM_FREE_UNPROTECT() SYS_ARCH_UNPROTECT(lev_free)
+#define LWIP_MEM_ALLOC_DECL_PROTECT() SYS_ARCH_DECL_PROTECT(lev_alloc)
+#define LWIP_MEM_ALLOC_PROTECT() SYS_ARCH_PROTECT(lev_alloc)
+#define LWIP_MEM_ALLOC_UNPROTECT() SYS_ARCH_UNPROTECT(lev_alloc)
+
+#else /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
+
+/* Protect the heap only by using a semaphore */
+#define LWIP_MEM_FREE_DECL_PROTECT()
+#define LWIP_MEM_FREE_PROTECT() sys_mutex_lock(&mem_mutex)
+#define LWIP_MEM_FREE_UNPROTECT() sys_mutex_unlock(&mem_mutex)
+/* mem_malloc is protected using semaphore AND LWIP_MEM_ALLOC_PROTECT */
+#define LWIP_MEM_ALLOC_DECL_PROTECT()
+#define LWIP_MEM_ALLOC_PROTECT()
+#define LWIP_MEM_ALLOC_UNPROTECT()
+
+#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
+
+
+/**
+ * "Plug holes" by combining adjacent empty struct mems.
+ * After this function is through, there should not exist
+ * one empty struct mem pointing to another empty struct mem.
+ *
+ * @param mem this points to a struct mem which just has been freed
+ * @internal this function is only called by mem_free() and mem_trim()
+ *
+ * This assumes access to the heap is protected by the calling function
+ * already.
+ */
+static void
+plug_holes(struct mem *mem)
+{
+ struct mem *nmem;
+ struct mem *pmem;
+
+ LWIP_ASSERT("plug_holes: mem >= ram", (u8_t *)mem >= ram);
+ LWIP_ASSERT("plug_holes: mem < ram_end", (u8_t *)mem < (u8_t *)ram_end);
+ LWIP_ASSERT("plug_holes: mem->used == 0", mem->used == 0);
+
+ /* plug hole forward */
+ LWIP_ASSERT("plug_holes: mem->next <= MEM_SIZE_ALIGNED", mem->next <= MEM_SIZE_ALIGNED);
+
+ nmem = (struct mem *)(void *)&ram[mem->next];
+ if (mem != nmem && nmem->used == 0 && (u8_t *)nmem != (u8_t *)ram_end) {
+ /* if mem->next is unused and not end of ram, combine mem and mem->next */
+ if (lfree == nmem) {
+ lfree = mem;
+ }
+ mem->next = nmem->next;
+ ((struct mem *)(void *)&ram[nmem->next])->prev = (mem_size_t)((u8_t *)mem - ram);
+ }
+
+ /* plug hole backward */
+ pmem = (struct mem *)(void *)&ram[mem->prev];
+ if (pmem != mem && pmem->used == 0) {
+ /* if mem->prev is unused, combine mem and mem->prev */
+ if (lfree == mem) {
+ lfree = pmem;
+ }
+ pmem->next = mem->next;
+ ((struct mem *)(void *)&ram[mem->next])->prev = (mem_size_t)((u8_t *)pmem - ram);
+ }
+}
+
+/**
+ * Zero the heap and initialize start, end and lowest-free
+ */
+void
+mem_init(void)
+{
+ struct mem *mem;
+
+ LWIP_ASSERT("Sanity check alignment",
+ (SIZEOF_STRUCT_MEM & (MEM_ALIGNMENT-1)) == 0);
+
+ /* align the heap */
+ ram = (u8_t *)LWIP_MEM_ALIGN(LWIP_RAM_HEAP_POINTER);
+ /* initialize the start of the heap */
+ mem = (struct mem *)(void *)ram;
+ mem->next = MEM_SIZE_ALIGNED;
+ mem->prev = 0;
+ mem->used = 0;
+ /* initialize the end of the heap */
+ ram_end = (struct mem *)(void *)&ram[MEM_SIZE_ALIGNED];
+ ram_end->used = 1;
+ ram_end->next = MEM_SIZE_ALIGNED;
+ ram_end->prev = MEM_SIZE_ALIGNED;
+
+ /* initialize the lowest-free pointer to the start of the heap */
+ lfree = (struct mem *)(void *)ram;
+
+ MEM_STATS_AVAIL(avail, MEM_SIZE_ALIGNED);
+
+ if (sys_mutex_new(&mem_mutex) != ERR_OK) {
+ LWIP_ASSERT("failed to create mem_mutex", 0);
+ }
+}
+
+/**
+ * Put a struct mem back on the heap
+ *
+ * @param rmem is the data portion of a struct mem as returned by a previous
+ * call to mem_malloc()
+ */
+void
+mem_free(void *rmem)
+{
+ struct mem *mem;
+ LWIP_MEM_FREE_DECL_PROTECT();
+
+ if (rmem == NULL) {
+ LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("mem_free(p == NULL) was called.\n"));
+ return;
+ }
+ LWIP_ASSERT("mem_free: sanity check alignment", (((mem_ptr_t)rmem) & (MEM_ALIGNMENT-1)) == 0);
+
+ LWIP_ASSERT("mem_free: legal memory", (u8_t *)rmem >= (u8_t *)ram &&
+ (u8_t *)rmem < (u8_t *)ram_end);
+
+ if ((u8_t *)rmem < (u8_t *)ram || (u8_t *)rmem >= (u8_t *)ram_end) {
+ SYS_ARCH_DECL_PROTECT(lev);
+ LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_LEVEL_SEVERE, ("mem_free: illegal memory\n"));
+ /* protect mem stats from concurrent access */
+ SYS_ARCH_PROTECT(lev);
+ MEM_STATS_INC(illegal);
+ SYS_ARCH_UNPROTECT(lev);
+ return;
+ }
+ /* protect the heap from concurrent access */
+ LWIP_MEM_FREE_PROTECT();
+ /* Get the corresponding struct mem ... */
+ /* cast through void* to get rid of alignment warnings */
+ mem = (struct mem *)(void *)((u8_t *)rmem - SIZEOF_STRUCT_MEM);
+ /* ... which has to be in a used state ... */
+ LWIP_ASSERT("mem_free: mem->used", mem->used);
+ /* ... and is now unused. */
+ mem->used = 0;
+
+ if (mem < lfree) {
+ /* the newly freed struct is now the lowest */
+ lfree = mem;
+ }
+
+ MEM_STATS_DEC_USED(used, mem->next - (mem_size_t)(((u8_t *)mem - ram)));
+
+ /* finally, see if prev or next are free also */
+ plug_holes(mem);
+#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
+ mem_free_count = 1;
+#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
+ LWIP_MEM_FREE_UNPROTECT();
+}
+
+/**
+ * Shrink memory returned by mem_malloc().
+ *
+ * @param rmem pointer to memory allocated by mem_malloc the is to be shrinked
+ * @param newsize required size after shrinking (needs to be smaller than or
+ * equal to the previous size)
+ * @return for compatibility reasons: is always == rmem, at the moment
+ * or NULL if newsize is > old size, in which case rmem is NOT touched
+ * or freed!
+ */
+void *
+mem_trim(void *rmem, mem_size_t newsize)
+{
+ mem_size_t size;
+ mem_size_t ptr, ptr2;
+ struct mem *mem, *mem2;
+ /* use the FREE_PROTECT here: it protects with sem OR SYS_ARCH_PROTECT */
+ LWIP_MEM_FREE_DECL_PROTECT();
+
+ /* Expand the size of the allocated memory region so that we can
+ adjust for alignment. */
+ newsize = LWIP_MEM_ALIGN_SIZE(newsize);
+
+ if (newsize < MIN_SIZE_ALIGNED) {
+ /* every data block must be at least MIN_SIZE_ALIGNED long */
+ newsize = MIN_SIZE_ALIGNED;
+ }
+
+ if (newsize > MEM_SIZE_ALIGNED) {
+ return NULL;
+ }
+
+ LWIP_ASSERT("mem_trim: legal memory", (u8_t *)rmem >= (u8_t *)ram &&
+ (u8_t *)rmem < (u8_t *)ram_end);
+
+ if ((u8_t *)rmem < (u8_t *)ram || (u8_t *)rmem >= (u8_t *)ram_end) {
+ SYS_ARCH_DECL_PROTECT(lev);
+ LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_LEVEL_SEVERE, ("mem_trim: illegal memory\n"));
+ /* protect mem stats from concurrent access */
+ SYS_ARCH_PROTECT(lev);
+ MEM_STATS_INC(illegal);
+ SYS_ARCH_UNPROTECT(lev);
+ return rmem;
+ }
+ /* Get the corresponding struct mem ... */
+ /* cast through void* to get rid of alignment warnings */
+ mem = (struct mem *)(void *)((u8_t *)rmem - SIZEOF_STRUCT_MEM);
+ /* ... and its offset pointer */
+ ptr = (mem_size_t)((u8_t *)mem - ram);
+
+ size = mem->next - ptr - SIZEOF_STRUCT_MEM;
+ LWIP_ASSERT("mem_trim can only shrink memory", newsize <= size);
+ if (newsize > size) {
+ /* not supported */
+ return NULL;
+ }
+ if (newsize == size) {
+ /* No change in size, simply return */
+ return rmem;
+ }
+
+ /* protect the heap from concurrent access */
+ LWIP_MEM_FREE_PROTECT();
+
+ mem2 = (struct mem *)(void *)&ram[mem->next];
+ if (mem2->used == 0) {
+ /* The next struct is unused, we can simply move it at little */
+ mem_size_t next;
+ /* remember the old next pointer */
+ next = mem2->next;
+ /* create new struct mem which is moved directly after the shrinked mem */
+ ptr2 = ptr + SIZEOF_STRUCT_MEM + newsize;
+ if (lfree == mem2) {
+ lfree = (struct mem *)(void *)&ram[ptr2];
+ }
+ mem2 = (struct mem *)(void *)&ram[ptr2];
+ mem2->used = 0;
+ /* restore the next pointer */
+ mem2->next = next;
+ /* link it back to mem */
+ mem2->prev = ptr;
+ /* link mem to it */
+ mem->next = ptr2;
+ /* last thing to restore linked list: as we have moved mem2,
+ * let 'mem2->next->prev' point to mem2 again. but only if mem2->next is not
+ * the end of the heap */
+ if (mem2->next != MEM_SIZE_ALIGNED) {
+ ((struct mem *)(void *)&ram[mem2->next])->prev = ptr2;
+ }
+ MEM_STATS_DEC_USED(used, (size - newsize));
+ /* no need to plug holes, we've already done that */
+ } else if (newsize + SIZEOF_STRUCT_MEM + MIN_SIZE_ALIGNED <= size) {
+ /* Next struct is used but there's room for another struct mem with
+ * at least MIN_SIZE_ALIGNED of data.
+ * Old size ('size') must be big enough to contain at least 'newsize' plus a struct mem
+ * ('SIZEOF_STRUCT_MEM') with some data ('MIN_SIZE_ALIGNED').
+ * @todo we could leave out MIN_SIZE_ALIGNED. We would create an empty
+ * region that couldn't hold data, but when mem->next gets freed,
+ * the 2 regions would be combined, resulting in more free memory */
+ ptr2 = ptr + SIZEOF_STRUCT_MEM + newsize;
+ mem2 = (struct mem *)(void *)&ram[ptr2];
+ if (mem2 < lfree) {
+ lfree = mem2;
+ }
+ mem2->used = 0;
+ mem2->next = mem->next;
+ mem2->prev = ptr;
+ mem->next = ptr2;
+ if (mem2->next != MEM_SIZE_ALIGNED) {
+ ((struct mem *)(void *)&ram[mem2->next])->prev = ptr2;
+ }
+ MEM_STATS_DEC_USED(used, (size - newsize));
+ /* the original mem->next is used, so no need to plug holes! */
+ }
+ /* else {
+ next struct mem is used but size between mem and mem2 is not big enough
+ to create another struct mem
+ -> don't do anyhting.
+ -> the remaining space stays unused since it is too small
+ } */
+#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
+ mem_free_count = 1;
+#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
+ LWIP_MEM_FREE_UNPROTECT();
+ return rmem;
+}
+
+/**
+ * Allocate a block of memory with a minimum of 'size' bytes.
+ *
+ * @param size is the minimum size of the requested block in bytes.
+ * @return pointer to allocated memory or NULL if no free memory was found.
+ *
+ * Note that the returned value will always be aligned (as defined by MEM_ALIGNMENT).
+ */
+void *
+mem_malloc(mem_size_t size)
+{
+ mem_size_t ptr, ptr2;
+ struct mem *mem, *mem2;
+#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
+ u8_t local_mem_free_count = 0;
+#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
+ LWIP_MEM_ALLOC_DECL_PROTECT();
+
+ if (size == 0) {
+ return NULL;
+ }
+
+ /* Expand the size of the allocated memory region so that we can
+ adjust for alignment. */
+ size = LWIP_MEM_ALIGN_SIZE(size);
+
+ if (size < MIN_SIZE_ALIGNED) {
+ /* every data block must be at least MIN_SIZE_ALIGNED long */
+ size = MIN_SIZE_ALIGNED;
+ }
+
+ if (size > MEM_SIZE_ALIGNED) {
+ return NULL;
+ }
+
+ /* protect the heap from concurrent access */
+ sys_mutex_lock(&mem_mutex);
+ LWIP_MEM_ALLOC_PROTECT();
+#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
+ /* run as long as a mem_free disturbed mem_malloc or mem_trim */
+ do {
+ local_mem_free_count = 0;
+#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
+
+ /* Scan through the heap searching for a free block that is big enough,
+ * beginning with the lowest free block.
+ */
+ for (ptr = (mem_size_t)((u8_t *)lfree - ram); ptr < MEM_SIZE_ALIGNED - size;
+ ptr = ((struct mem *)(void *)&ram[ptr])->next) {
+ mem = (struct mem *)(void *)&ram[ptr];
+#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
+ mem_free_count = 0;
+ LWIP_MEM_ALLOC_UNPROTECT();
+ /* allow mem_free or mem_trim to run */
+ LWIP_MEM_ALLOC_PROTECT();
+ if (mem_free_count != 0) {
+ /* If mem_free or mem_trim have run, we have to restart since they
+ could have altered our current struct mem. */
+ local_mem_free_count = 1;
+ break;
+ }
+#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
+
+ if ((!mem->used) &&
+ (mem->next - (ptr + SIZEOF_STRUCT_MEM)) >= size) {
+ /* mem is not used and at least perfect fit is possible:
+ * mem->next - (ptr + SIZEOF_STRUCT_MEM) gives us the 'user data size' of mem */
+
+ if (mem->next - (ptr + SIZEOF_STRUCT_MEM) >= (size + SIZEOF_STRUCT_MEM + MIN_SIZE_ALIGNED)) {
+ /* (in addition to the above, we test if another struct mem (SIZEOF_STRUCT_MEM) containing
+ * at least MIN_SIZE_ALIGNED of data also fits in the 'user data space' of 'mem')
+ * -> split large block, create empty remainder,
+ * remainder must be large enough to contain MIN_SIZE_ALIGNED data: if
+ * mem->next - (ptr + (2*SIZEOF_STRUCT_MEM)) == size,
+ * struct mem would fit in but no data between mem2 and mem2->next
+ * @todo we could leave out MIN_SIZE_ALIGNED. We would create an empty
+ * region that couldn't hold data, but when mem->next gets freed,
+ * the 2 regions would be combined, resulting in more free memory
+ */
+ ptr2 = ptr + SIZEOF_STRUCT_MEM + size;
+ /* create mem2 struct */
+ mem2 = (struct mem *)(void *)&ram[ptr2];
+ mem2->used = 0;
+ mem2->next = mem->next;
+ mem2->prev = ptr;
+ /* and insert it between mem and mem->next */
+ mem->next = ptr2;
+ mem->used = 1;
+
+ if (mem2->next != MEM_SIZE_ALIGNED) {
+ ((struct mem *)(void *)&ram[mem2->next])->prev = ptr2;
+ }
+ MEM_STATS_INC_USED(used, (size + SIZEOF_STRUCT_MEM));
+ } else {
+ /* (a mem2 struct does no fit into the user data space of mem and mem->next will always
+ * be used at this point: if not we have 2 unused structs in a row, plug_holes should have
+ * take care of this).
+ * -> near fit or exact fit: do not split, no mem2 creation
+ * also can't move mem->next directly behind mem, since mem->next
+ * will always be used at this point!
+ */
+ mem->used = 1;
+ MEM_STATS_INC_USED(used, mem->next - (mem_size_t)((u8_t *)mem - ram));
+ }
+#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
+mem_malloc_adjust_lfree:
+#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
+ if (mem == lfree) {
+ struct mem *cur = lfree;
+ /* Find next free block after mem and update lowest free pointer */
+ while (cur->used && cur != ram_end) {
+#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
+ mem_free_count = 0;
+ LWIP_MEM_ALLOC_UNPROTECT();
+ /* prevent high interrupt latency... */
+ LWIP_MEM_ALLOC_PROTECT();
+ if (mem_free_count != 0) {
+ /* If mem_free or mem_trim have run, we have to restart since they
+ could have altered our current struct mem or lfree. */
+ goto mem_malloc_adjust_lfree;
+ }
+#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
+ cur = (struct mem *)(void *)&ram[cur->next];
+ }
+ lfree = cur;
+ LWIP_ASSERT("mem_malloc: !lfree->used", ((lfree == ram_end) || (!lfree->used)));
+ }
+ LWIP_MEM_ALLOC_UNPROTECT();
+ sys_mutex_unlock(&mem_mutex);
+ LWIP_ASSERT("mem_malloc: allocated memory not above ram_end.",
+ (mem_ptr_t)mem + SIZEOF_STRUCT_MEM + size <= (mem_ptr_t)ram_end);
+ LWIP_ASSERT("mem_malloc: allocated memory properly aligned.",
+ ((mem_ptr_t)mem + SIZEOF_STRUCT_MEM) % MEM_ALIGNMENT == 0);
+ LWIP_ASSERT("mem_malloc: sanity check alignment",
+ (((mem_ptr_t)mem) & (MEM_ALIGNMENT-1)) == 0);
+
+ return (u8_t *)mem + SIZEOF_STRUCT_MEM;
+ }
+ }
+#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
+ /* if we got interrupted by a mem_free, try again */
+ } while (local_mem_free_count != 0);
+#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
+ LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("mem_malloc: could not allocate %"S16_F" bytes\n", (s16_t)size));
+ MEM_STATS_INC(err);
+ LWIP_MEM_ALLOC_UNPROTECT();
+ sys_mutex_unlock(&mem_mutex);
+ return NULL;
+}
+
+#endif /* MEM_USE_POOLS */
+
+#if MEM_LIBC_MALLOC && (!LWIP_STATS || !MEM_STATS)
+void *
+mem_calloc(mem_size_t count, mem_size_t size)
+{
+ return mem_clib_calloc(count, size);
+}
+
+#else /* MEM_LIBC_MALLOC && (!LWIP_STATS || !MEM_STATS) */
+/**
+ * Contiguously allocates enough space for count objects that are size bytes
+ * of memory each and returns a pointer to the allocated memory.
+ *
+ * The allocated memory is filled with bytes of value zero.
+ *
+ * @param count number of objects to allocate
+ * @param size size of the objects to allocate
+ * @return pointer to allocated memory / NULL pointer if there is an error
+ */
+void *
+mem_calloc(mem_size_t count, mem_size_t size)
+{
+ void *p;
+
+ /* allocate 'count' objects of size 'size' */
+ p = mem_malloc(count * size);
+ if (p) {
+ /* zero the memory */
+ memset(p, 0, (size_t)count * (size_t)size);
+ }
+ return p;
+}
+#endif /* MEM_LIBC_MALLOC && (!LWIP_STATS || !MEM_STATS) */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/memp.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/memp.c
new file mode 100644
index 0000000..58fab1a
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/memp.c
@@ -0,0 +1,496 @@
+/**
+ * @file
+ * Dynamic pool memory manager
+ *
+ * lwIP has dedicated pools for many structures (netconn, protocol control blocks,
+ * packet buffers, ...). All these pools are managed here.
+ *
+ * @defgroup mempool Memory pools
+ * @ingroup infrastructure
+ * Custom memory pools
+
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#include "lwip/memp.h"
+#include "lwip/sys.h"
+#include "lwip/stats.h"
+
+#include <string.h>
+
+/* Make sure we include everything we need for size calculation required by memp_std.h */
+#include "lwip/pbuf.h"
+#include "lwip/raw.h"
+#include "lwip/udp.h"
+#include "lwip/tcp.h"
+#include "lwip/priv/tcp_priv.h"
+#include "lwip/ip4_frag.h"
+#include "lwip/netbuf.h"
+#include "lwip/api.h"
+#include "lwip/priv/tcpip_priv.h"
+#include "lwip/priv/api_msg.h"
+#include "lwip/sockets.h"
+#include "lwip/netifapi.h"
+#include "lwip/etharp.h"
+#include "lwip/igmp.h"
+#include "lwip/timeouts.h"
+/* needed by default MEMP_NUM_SYS_TIMEOUT */
+#include "netif/ppp/ppp_opts.h"
+#include "lwip/netdb.h"
+#include "lwip/dns.h"
+#include "lwip/priv/nd6_priv.h"
+#include "lwip/ip6_frag.h"
+#include "lwip/mld6.h"
+
+#define LWIP_MEMPOOL(name,num,size,desc) LWIP_MEMPOOL_DECLARE(name,num,size,desc)
+#include "lwip/priv/memp_std.h"
+
+const struct memp_desc* const memp_pools[MEMP_MAX] = {
+#define LWIP_MEMPOOL(name,num,size,desc) &memp_ ## name,
+#include "lwip/priv/memp_std.h"
+};
+
+#ifdef LWIP_HOOK_FILENAME
+#include LWIP_HOOK_FILENAME
+#endif
+
+#if MEMP_MEM_MALLOC && MEMP_OVERFLOW_CHECK >= 2
+#undef MEMP_OVERFLOW_CHECK
+/* MEMP_OVERFLOW_CHECK >= 2 does not work with MEMP_MEM_MALLOC, use 1 instead */
+#define MEMP_OVERFLOW_CHECK 1
+#endif
+
+#if MEMP_SANITY_CHECK && !MEMP_MEM_MALLOC
+/**
+ * Check that memp-lists don't form a circle, using "Floyd's cycle-finding algorithm".
+ */
+static int
+memp_sanity(const struct memp_desc *desc)
+{
+ struct memp *t, *h;
+
+ t = *desc->tab;
+ if (t != NULL) {
+ for (h = t->next; (t != NULL) && (h != NULL); t = t->next,
+ h = ((h->next != NULL) ? h->next->next : NULL)) {
+ if (t == h) {
+ return 0;
+ }
+ }
+ }
+
+ return 1;
+}
+#endif /* MEMP_SANITY_CHECK && !MEMP_MEM_MALLOC */
+
+#if MEMP_OVERFLOW_CHECK
+/**
+ * Check if a memp element was victim of an overflow
+ * (e.g. the restricted area after it has been altered)
+ *
+ * @param p the memp element to check
+ * @param desc the pool p comes from
+ */
+static void
+memp_overflow_check_element_overflow(struct memp *p, const struct memp_desc *desc)
+{
+#if MEMP_SANITY_REGION_AFTER_ALIGNED > 0
+ u16_t k;
+ u8_t *m;
+ m = (u8_t*)p + MEMP_SIZE + desc->size;
+ for (k = 0; k < MEMP_SANITY_REGION_AFTER_ALIGNED; k++) {
+ if (m[k] != 0xcd) {
+ char errstr[128] = "detected memp overflow in pool ";
+ strcat(errstr, desc->desc);
+ LWIP_ASSERT(errstr, 0);
+ }
+ }
+#else /* MEMP_SANITY_REGION_AFTER_ALIGNED > 0 */
+ LWIP_UNUSED_ARG(p);
+ LWIP_UNUSED_ARG(desc);
+#endif /* MEMP_SANITY_REGION_AFTER_ALIGNED > 0 */
+}
+
+/**
+ * Check if a memp element was victim of an underflow
+ * (e.g. the restricted area before it has been altered)
+ *
+ * @param p the memp element to check
+ * @param desc the pool p comes from
+ */
+static void
+memp_overflow_check_element_underflow(struct memp *p, const struct memp_desc *desc)
+{
+#if MEMP_SANITY_REGION_BEFORE_ALIGNED > 0
+ u16_t k;
+ u8_t *m;
+ m = (u8_t*)p + MEMP_SIZE - MEMP_SANITY_REGION_BEFORE_ALIGNED;
+ for (k = 0; k < MEMP_SANITY_REGION_BEFORE_ALIGNED; k++) {
+ if (m[k] != 0xcd) {
+ char errstr[128] = "detected memp underflow in pool ";
+ strcat(errstr, desc->desc);
+ LWIP_ASSERT(errstr, 0);
+ }
+ }
+#else /* MEMP_SANITY_REGION_BEFORE_ALIGNED > 0 */
+ LWIP_UNUSED_ARG(p);
+ LWIP_UNUSED_ARG(desc);
+#endif /* MEMP_SANITY_REGION_BEFORE_ALIGNED > 0 */
+}
+
+/**
+ * Initialize the restricted area of on memp element.
+ */
+static void
+memp_overflow_init_element(struct memp *p, const struct memp_desc *desc)
+{
+#if MEMP_SANITY_REGION_BEFORE_ALIGNED > 0 || MEMP_SANITY_REGION_AFTER_ALIGNED > 0
+ u8_t *m;
+#if MEMP_SANITY_REGION_BEFORE_ALIGNED > 0
+ m = (u8_t*)p + MEMP_SIZE - MEMP_SANITY_REGION_BEFORE_ALIGNED;
+ memset(m, 0xcd, MEMP_SANITY_REGION_BEFORE_ALIGNED);
+#endif
+#if MEMP_SANITY_REGION_AFTER_ALIGNED > 0
+ m = (u8_t*)p + MEMP_SIZE + desc->size;
+ memset(m, 0xcd, MEMP_SANITY_REGION_AFTER_ALIGNED);
+#endif
+#else /* MEMP_SANITY_REGION_BEFORE_ALIGNED > 0 || MEMP_SANITY_REGION_AFTER_ALIGNED > 0 */
+ LWIP_UNUSED_ARG(p);
+ LWIP_UNUSED_ARG(desc);
+#endif /* MEMP_SANITY_REGION_BEFORE_ALIGNED > 0 || MEMP_SANITY_REGION_AFTER_ALIGNED > 0 */
+}
+
+#if MEMP_OVERFLOW_CHECK >= 2
+/**
+ * Do an overflow check for all elements in every pool.
+ *
+ * @see memp_overflow_check_element for a description of the check
+ */
+static void
+memp_overflow_check_all(void)
+{
+ u16_t i, j;
+ struct memp *p;
+ SYS_ARCH_DECL_PROTECT(old_level);
+ SYS_ARCH_PROTECT(old_level);
+
+ for (i = 0; i < MEMP_MAX; ++i) {
+ p = (struct memp*)LWIP_MEM_ALIGN(memp_pools[i]->base);
+ for (j = 0; j < memp_pools[i]->num; ++j) {
+ memp_overflow_check_element_overflow(p, memp_pools[i]);
+ memp_overflow_check_element_underflow(p, memp_pools[i]);
+ p = LWIP_ALIGNMENT_CAST(struct memp*, ((u8_t*)p + MEMP_SIZE + memp_pools[i]->size + MEMP_SANITY_REGION_AFTER_ALIGNED));
+ }
+ }
+ SYS_ARCH_UNPROTECT(old_level);
+}
+#endif /* MEMP_OVERFLOW_CHECK >= 2 */
+#endif /* MEMP_OVERFLOW_CHECK */
+
+/**
+ * Initialize custom memory pool.
+ * Related functions: memp_malloc_pool, memp_free_pool
+ *
+ * @param desc pool to initialize
+ */
+void
+memp_init_pool(const struct memp_desc *desc)
+{
+#if MEMP_MEM_MALLOC
+ LWIP_UNUSED_ARG(desc);
+#else
+ int i;
+ struct memp *memp;
+
+ *desc->tab = NULL;
+ memp = (struct memp*)LWIP_MEM_ALIGN(desc->base);
+ /* create a linked list of memp elements */
+ for (i = 0; i < desc->num; ++i) {
+ memp->next = *desc->tab;
+ *desc->tab = memp;
+#if MEMP_OVERFLOW_CHECK
+ memp_overflow_init_element(memp, desc);
+#endif /* MEMP_OVERFLOW_CHECK */
+ /* cast through void* to get rid of alignment warnings */
+ memp = (struct memp *)(void *)((u8_t *)memp + MEMP_SIZE + desc->size
+#if MEMP_OVERFLOW_CHECK
+ + MEMP_SANITY_REGION_AFTER_ALIGNED
+#endif
+ );
+ }
+#if MEMP_STATS
+ desc->stats->avail = desc->num;
+#endif /* MEMP_STATS */
+#endif /* !MEMP_MEM_MALLOC */
+
+#if MEMP_STATS && (defined(LWIP_DEBUG) || LWIP_STATS_DISPLAY)
+ desc->stats->name = desc->desc;
+#endif /* MEMP_STATS && (defined(LWIP_DEBUG) || LWIP_STATS_DISPLAY) */
+}
+
+/**
+ * Initializes lwIP built-in pools.
+ * Related functions: memp_malloc, memp_free
+ *
+ * Carves out memp_memory into linked lists for each pool-type.
+ */
+void
+memp_init(void)
+{
+ u16_t i;
+
+ /* for every pool: */
+ for (i = 0; i < LWIP_ARRAYSIZE(memp_pools); i++) {
+ memp_init_pool(memp_pools[i]);
+
+#if LWIP_STATS && MEMP_STATS
+ lwip_stats.memp[i] = memp_pools[i]->stats;
+#endif
+ }
+
+#if MEMP_OVERFLOW_CHECK >= 2
+ /* check everything a first time to see if it worked */
+ memp_overflow_check_all();
+#endif /* MEMP_OVERFLOW_CHECK >= 2 */
+}
+
+static void*
+#if !MEMP_OVERFLOW_CHECK
+do_memp_malloc_pool(const struct memp_desc *desc)
+#else
+do_memp_malloc_pool_fn(const struct memp_desc *desc, const char* file, const int line)
+#endif
+{
+ struct memp *memp;
+ SYS_ARCH_DECL_PROTECT(old_level);
+
+#if MEMP_MEM_MALLOC
+ memp = (struct memp *)mem_malloc(MEMP_SIZE + MEMP_ALIGN_SIZE(desc->size));
+ SYS_ARCH_PROTECT(old_level);
+#else /* MEMP_MEM_MALLOC */
+ SYS_ARCH_PROTECT(old_level);
+
+ memp = *desc->tab;
+#endif /* MEMP_MEM_MALLOC */
+
+ if (memp != NULL) {
+#if !MEMP_MEM_MALLOC
+#if MEMP_OVERFLOW_CHECK == 1
+ memp_overflow_check_element_overflow(memp, desc);
+ memp_overflow_check_element_underflow(memp, desc);
+#endif /* MEMP_OVERFLOW_CHECK */
+
+ *desc->tab = memp->next;
+#if MEMP_OVERFLOW_CHECK
+ memp->next = NULL;
+#endif /* MEMP_OVERFLOW_CHECK */
+#endif /* !MEMP_MEM_MALLOC */
+#if MEMP_OVERFLOW_CHECK
+ memp->file = file;
+ memp->line = line;
+#if MEMP_MEM_MALLOC
+ memp_overflow_init_element(memp, desc);
+#endif /* MEMP_MEM_MALLOC */
+#endif /* MEMP_OVERFLOW_CHECK */
+ LWIP_ASSERT("memp_malloc: memp properly aligned",
+ ((mem_ptr_t)memp % MEM_ALIGNMENT) == 0);
+#if MEMP_STATS
+ desc->stats->used++;
+ if (desc->stats->used > desc->stats->max) {
+ desc->stats->max = desc->stats->used;
+ }
+#endif
+ SYS_ARCH_UNPROTECT(old_level);
+ /* cast through u8_t* to get rid of alignment warnings */
+ return ((u8_t*)memp + MEMP_SIZE);
+ } else {
+ LWIP_DEBUGF(MEMP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("memp_malloc: out of memory in pool %s\n", desc->desc));
+#if MEMP_STATS
+ desc->stats->err++;
+#endif
+ }
+
+ SYS_ARCH_UNPROTECT(old_level);
+ return NULL;
+}
+
+/**
+ * Get an element from a custom pool.
+ *
+ * @param desc the pool to get an element from
+ *
+ * @return a pointer to the allocated memory or a NULL pointer on error
+ */
+void *
+#if !MEMP_OVERFLOW_CHECK
+memp_malloc_pool(const struct memp_desc *desc)
+#else
+memp_malloc_pool_fn(const struct memp_desc *desc, const char* file, const int line)
+#endif
+{
+ LWIP_ASSERT("invalid pool desc", desc != NULL);
+ if (desc == NULL) {
+ return NULL;
+ }
+
+#if !MEMP_OVERFLOW_CHECK
+ return do_memp_malloc_pool(desc);
+#else
+ return do_memp_malloc_pool_fn(desc, file, line);
+#endif
+}
+
+/**
+ * Get an element from a specific pool.
+ *
+ * @param type the pool to get an element from
+ *
+ * @return a pointer to the allocated memory or a NULL pointer on error
+ */
+void *
+#if !MEMP_OVERFLOW_CHECK
+memp_malloc(memp_t type)
+#else
+memp_malloc_fn(memp_t type, const char* file, const int line)
+#endif
+{
+ void *memp;
+ LWIP_ERROR("memp_malloc: type < MEMP_MAX", (type < MEMP_MAX), return NULL;);
+
+#if MEMP_OVERFLOW_CHECK >= 2
+ memp_overflow_check_all();
+#endif /* MEMP_OVERFLOW_CHECK >= 2 */
+
+#if !MEMP_OVERFLOW_CHECK
+ memp = do_memp_malloc_pool(memp_pools[type]);
+#else
+ memp = do_memp_malloc_pool_fn(memp_pools[type], file, line);
+#endif
+
+ return memp;
+}
+
+static void
+do_memp_free_pool(const struct memp_desc* desc, void *mem)
+{
+ struct memp *memp;
+ SYS_ARCH_DECL_PROTECT(old_level);
+
+ LWIP_ASSERT("memp_free: mem properly aligned",
+ ((mem_ptr_t)mem % MEM_ALIGNMENT) == 0);
+
+ /* cast through void* to get rid of alignment warnings */
+ memp = (struct memp *)(void *)((u8_t*)mem - MEMP_SIZE);
+
+ SYS_ARCH_PROTECT(old_level);
+
+#if MEMP_OVERFLOW_CHECK == 1
+ memp_overflow_check_element_overflow(memp, desc);
+ memp_overflow_check_element_underflow(memp, desc);
+#endif /* MEMP_OVERFLOW_CHECK */
+
+#if MEMP_STATS
+ desc->stats->used--;
+#endif
+
+#if MEMP_MEM_MALLOC
+ LWIP_UNUSED_ARG(desc);
+ SYS_ARCH_UNPROTECT(old_level);
+ mem_free(memp);
+#else /* MEMP_MEM_MALLOC */
+ memp->next = *desc->tab;
+ *desc->tab = memp;
+
+#if MEMP_SANITY_CHECK
+ LWIP_ASSERT("memp sanity", memp_sanity(desc));
+#endif /* MEMP_SANITY_CHECK */
+
+ SYS_ARCH_UNPROTECT(old_level);
+#endif /* !MEMP_MEM_MALLOC */
+}
+
+/**
+ * Put a custom pool element back into its pool.
+ *
+ * @param desc the pool where to put mem
+ * @param mem the memp element to free
+ */
+void
+memp_free_pool(const struct memp_desc* desc, void *mem)
+{
+ LWIP_ASSERT("invalid pool desc", desc != NULL);
+ if ((desc == NULL) || (mem == NULL)) {
+ return;
+ }
+
+ do_memp_free_pool(desc, mem);
+}
+
+/**
+ * Put an element back into its pool.
+ *
+ * @param type the pool where to put mem
+ * @param mem the memp element to free
+ */
+void
+memp_free(memp_t type, void *mem)
+{
+#ifdef LWIP_HOOK_MEMP_AVAILABLE
+ struct memp *old_first;
+#endif
+
+ LWIP_ERROR("memp_free: type < MEMP_MAX", (type < MEMP_MAX), return;);
+
+ if (mem == NULL) {
+ return;
+ }
+
+#if MEMP_OVERFLOW_CHECK >= 2
+ memp_overflow_check_all();
+#endif /* MEMP_OVERFLOW_CHECK >= 2 */
+
+#ifdef LWIP_HOOK_MEMP_AVAILABLE
+ old_first = *memp_pools[type]->tab;
+#endif
+
+ do_memp_free_pool(memp_pools[type], mem);
+
+#ifdef LWIP_HOOK_MEMP_AVAILABLE
+ if (old_first == NULL) {
+ LWIP_HOOK_MEMP_AVAILABLE(type);
+ }
+#endif
+}
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/netif.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/netif.c
new file mode 100644
index 0000000..428b148
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/netif.c
@@ -0,0 +1,1265 @@
+/**
+ * @file
+ * lwIP network interface abstraction
+ *
+ * @defgroup netif Network interface (NETIF)
+ * @ingroup callbackstyle_api
+ *
+ * @defgroup netif_ip4 IPv4 address handling
+ * @ingroup netif
+ *
+ * @defgroup netif_ip6 IPv6 address handling
+ * @ingroup netif
+ *
+ * @defgroup netif_cd Client data handling
+ * Store data (void*) on a netif for application usage.
+ * @see @ref LWIP_NUM_NETIF_CLIENT_DATA
+ * @ingroup netif
+ */
+
+/*
+ * 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 <string.h>
+
+#include "lwip/def.h"
+#include "lwip/ip_addr.h"
+#include "lwip/ip6_addr.h"
+#include "lwip/netif.h"
+#include "lwip/priv/tcp_priv.h"
+#include "lwip/udp.h"
+#include "lwip/raw.h"
+#include "lwip/snmp.h"
+#include "lwip/igmp.h"
+#include "lwip/etharp.h"
+#include "lwip/stats.h"
+#include "lwip/sys.h"
+#include "lwip/ip.h"
+#if ENABLE_LOOPBACK
+#if LWIP_NETIF_LOOPBACK_MULTITHREADING
+#include "lwip/tcpip.h"
+#endif /* LWIP_NETIF_LOOPBACK_MULTITHREADING */
+#endif /* ENABLE_LOOPBACK */
+
+#include "netif/ethernet.h"
+
+#if LWIP_AUTOIP
+#include "lwip/autoip.h"
+#endif /* LWIP_AUTOIP */
+#if LWIP_DHCP
+#include "lwip/dhcp.h"
+#endif /* LWIP_DHCP */
+#if LWIP_IPV6_DHCP6
+#include "lwip/dhcp6.h"
+#endif /* LWIP_IPV6_DHCP6 */
+#if LWIP_IPV6_MLD
+#include "lwip/mld6.h"
+#endif /* LWIP_IPV6_MLD */
+#if LWIP_IPV6
+#include "lwip/nd6.h"
+#endif
+
+#if LWIP_NETIF_STATUS_CALLBACK
+#define NETIF_STATUS_CALLBACK(n) do{ if (n->status_callback) { (n->status_callback)(n); }}while(0)
+#else
+#define NETIF_STATUS_CALLBACK(n)
+#endif /* LWIP_NETIF_STATUS_CALLBACK */
+
+#if LWIP_NETIF_LINK_CALLBACK
+#define NETIF_LINK_CALLBACK(n) do{ if (n->link_callback) { (n->link_callback)(n); }}while(0)
+#else
+#define NETIF_LINK_CALLBACK(n)
+#endif /* LWIP_NETIF_LINK_CALLBACK */
+
+struct netif *netif_list;
+struct netif *netif_default;
+
+static u8_t netif_num;
+
+#if LWIP_NUM_NETIF_CLIENT_DATA > 0
+static u8_t netif_client_id;
+#endif
+
+#define NETIF_REPORT_TYPE_IPV4 0x01
+#define NETIF_REPORT_TYPE_IPV6 0x02
+static void netif_issue_reports(struct netif* netif, u8_t report_type);
+
+#if LWIP_IPV6
+static err_t netif_null_output_ip6(struct netif *netif, struct pbuf *p, const ip6_addr_t *ipaddr);
+#endif /* LWIP_IPV6 */
+
+#if LWIP_HAVE_LOOPIF
+#if LWIP_IPV4
+static err_t netif_loop_output_ipv4(struct netif *netif, struct pbuf *p, const ip4_addr_t* addr);
+#endif
+#if LWIP_IPV6
+static err_t netif_loop_output_ipv6(struct netif *netif, struct pbuf *p, const ip6_addr_t* addr);
+#endif
+
+
+static struct netif loop_netif;
+
+/**
+ * Initialize a lwip network interface structure for a loopback interface
+ *
+ * @param netif the lwip network interface structure for this loopif
+ * @return ERR_OK if the loopif is initialized
+ * ERR_MEM if private data couldn't be allocated
+ */
+static err_t
+netif_loopif_init(struct netif *netif)
+{
+ /* initialize the snmp variables and counters inside the struct netif
+ * ifSpeed: no assumption can be made!
+ */
+ MIB2_INIT_NETIF(netif, snmp_ifType_softwareLoopback, 0);
+
+ netif->name[0] = 'l';
+ netif->name[1] = 'o';
+#if LWIP_IPV4
+ netif->output = netif_loop_output_ipv4;
+#endif
+#if LWIP_IPV6
+ netif->output_ip6 = netif_loop_output_ipv6;
+#endif
+#if LWIP_LOOPIF_MULTICAST
+ netif->flags |= NETIF_FLAG_IGMP;
+#endif
+ return ERR_OK;
+}
+#endif /* LWIP_HAVE_LOOPIF */
+
+void
+netif_init(void)
+{
+#if LWIP_HAVE_LOOPIF
+#if LWIP_IPV4
+#define LOOPIF_ADDRINIT &loop_ipaddr, &loop_netmask, &loop_gw,
+ ip4_addr_t loop_ipaddr, loop_netmask, loop_gw;
+ IP4_ADDR(&loop_gw, 127,0,0,1);
+ IP4_ADDR(&loop_ipaddr, 127,0,0,1);
+ IP4_ADDR(&loop_netmask, 255,0,0,0);
+#else /* LWIP_IPV4 */
+#define LOOPIF_ADDRINIT
+#endif /* LWIP_IPV4 */
+
+#if NO_SYS
+ netif_add(&loop_netif, LOOPIF_ADDRINIT NULL, netif_loopif_init, ip_input);
+#else /* NO_SYS */
+ netif_add(&loop_netif, LOOPIF_ADDRINIT NULL, netif_loopif_init, tcpip_input);
+#endif /* NO_SYS */
+
+#if LWIP_IPV6
+ IP_ADDR6_HOST(loop_netif.ip6_addr, 0, 0, 0, 0x00000001UL);
+ loop_netif.ip6_addr_state[0] = IP6_ADDR_VALID;
+#endif /* LWIP_IPV6 */
+
+ netif_set_link_up(&loop_netif);
+ netif_set_up(&loop_netif);
+
+#endif /* LWIP_HAVE_LOOPIF */
+}
+
+/**
+ * @ingroup lwip_nosys
+ * Forwards a received packet for input processing with
+ * ethernet_input() or ip_input() depending on netif flags.
+ * Don't call directly, pass to netif_add() and call
+ * netif->input().
+ * Only works if the netif driver correctly sets
+ * NETIF_FLAG_ETHARP and/or NETIF_FLAG_ETHERNET flag!
+ */
+err_t
+netif_input(struct pbuf *p, struct netif *inp)
+{
+#if LWIP_ETHERNET
+ if (inp->flags & (NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET)) {
+ return ethernet_input(p, inp);
+ } else
+#endif /* LWIP_ETHERNET */
+ return ip_input(p, inp);
+}
+
+/**
+ * @ingroup netif
+ * Add a network interface to the list of lwIP netifs.
+ *
+ * @param netif a pre-allocated netif structure
+ * @param ipaddr IP address for the new netif
+ * @param netmask network mask for the new netif
+ * @param gw default gateway IP address for the new netif
+ * @param state opaque data passed to the new netif
+ * @param init callback function that initializes the interface
+ * @param input callback function that is called to pass
+ * ingress packets up in the protocol layer stack.\n
+ * It is recommended to use a function that passes the input directly
+ * to the stack (netif_input(), NO_SYS=1 mode) or via sending a
+ * message to TCPIP thread (tcpip_input(), NO_SYS=0 mode).\n
+ * These functions use netif flags NETIF_FLAG_ETHARP and NETIF_FLAG_ETHERNET
+ * to decide whether to forward to ethernet_input() or ip_input().
+ * In other words, the functions only work when the netif
+ * driver is implemented correctly!\n
+ * Most members of struct netif should be be initialized by the
+ * netif init function = netif driver (init parameter of this function).\n
+ * IPv6: Don't forget to call netif_create_ip6_linklocal_address() after
+ * setting the MAC address in struct netif.hwaddr
+ * (IPv6 requires a link-local address).
+ *
+ * @return netif, or NULL if failed.
+ */
+struct netif *
+netif_add(struct netif *netif,
+#if LWIP_IPV4
+ const ip4_addr_t *ipaddr, const ip4_addr_t *netmask, const ip4_addr_t *gw,
+#endif /* LWIP_IPV4 */
+ void *state, netif_init_fn init, netif_input_fn input)
+{
+#if LWIP_IPV6
+ s8_t i;
+#endif
+
+ LWIP_ASSERT("No init function given", init != NULL);
+
+ /* reset new interface configuration state */
+#if LWIP_IPV4
+ ip_addr_set_zero_ip4(&netif->ip_addr);
+ ip_addr_set_zero_ip4(&netif->netmask);
+ ip_addr_set_zero_ip4(&netif->gw);
+#endif /* LWIP_IPV4 */
+#if LWIP_IPV6
+ for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+ ip_addr_set_zero_ip6(&netif->ip6_addr[i]);
+ netif->ip6_addr_state[i] = IP6_ADDR_INVALID;
+ }
+ netif->output_ip6 = netif_null_output_ip6;
+#endif /* LWIP_IPV6 */
+ NETIF_SET_CHECKSUM_CTRL(netif, NETIF_CHECKSUM_ENABLE_ALL);
+ netif->flags = 0;
+#ifdef netif_get_client_data
+ memset(netif->client_data, 0, sizeof(netif->client_data));
+#endif /* LWIP_NUM_NETIF_CLIENT_DATA */
+#if LWIP_IPV6_AUTOCONFIG
+ /* IPv6 address autoconfiguration not enabled by default */
+ netif->ip6_autoconfig_enabled = 0;
+#endif /* LWIP_IPV6_AUTOCONFIG */
+#if LWIP_IPV6_SEND_ROUTER_SOLICIT
+ netif->rs_count = LWIP_ND6_MAX_MULTICAST_SOLICIT;
+#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */
+#if LWIP_NETIF_STATUS_CALLBACK
+ netif->status_callback = NULL;
+#endif /* LWIP_NETIF_STATUS_CALLBACK */
+#if LWIP_NETIF_LINK_CALLBACK
+ netif->link_callback = NULL;
+#endif /* LWIP_NETIF_LINK_CALLBACK */
+#if LWIP_IGMP
+ netif->igmp_mac_filter = NULL;
+#endif /* LWIP_IGMP */
+#if LWIP_IPV6 && LWIP_IPV6_MLD
+ netif->mld_mac_filter = NULL;
+#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */
+#if ENABLE_LOOPBACK
+ netif->loop_first = NULL;
+ netif->loop_last = NULL;
+#endif /* ENABLE_LOOPBACK */
+
+ /* remember netif specific state information data */
+ netif->state = state;
+ netif->num = netif_num++;
+ netif->input = input;
+
+ NETIF_SET_HWADDRHINT(netif, NULL);
+#if ENABLE_LOOPBACK && LWIP_LOOPBACK_MAX_PBUFS
+ netif->loop_cnt_current = 0;
+#endif /* ENABLE_LOOPBACK && LWIP_LOOPBACK_MAX_PBUFS */
+
+#if LWIP_IPV4
+ netif_set_addr(netif, ipaddr, netmask, gw);
+#endif /* LWIP_IPV4 */
+
+ /* call user specified initialization function for netif */
+ if (init(netif) != ERR_OK) {
+ return NULL;
+ }
+
+ /* add this netif to the list */
+ netif->next = netif_list;
+ netif_list = netif;
+ mib2_netif_added(netif);
+
+#if LWIP_IGMP
+ /* start IGMP processing */
+ if (netif->flags & NETIF_FLAG_IGMP) {
+ igmp_start(netif);
+ }
+#endif /* LWIP_IGMP */
+
+ LWIP_DEBUGF(NETIF_DEBUG, ("netif: added interface %c%c IP",
+ netif->name[0], netif->name[1]));
+#if LWIP_IPV4
+ LWIP_DEBUGF(NETIF_DEBUG, (" addr "));
+ ip4_addr_debug_print(NETIF_DEBUG, ipaddr);
+ LWIP_DEBUGF(NETIF_DEBUG, (" netmask "));
+ ip4_addr_debug_print(NETIF_DEBUG, netmask);
+ LWIP_DEBUGF(NETIF_DEBUG, (" gw "));
+ ip4_addr_debug_print(NETIF_DEBUG, gw);
+#endif /* LWIP_IPV4 */
+ LWIP_DEBUGF(NETIF_DEBUG, ("\n"));
+ return netif;
+}
+
+#if LWIP_IPV4
+/**
+ * @ingroup netif_ip4
+ * Change IP address configuration for a network interface (including netmask
+ * and default gateway).
+ *
+ * @param netif the network interface to change
+ * @param ipaddr the new IP address
+ * @param netmask the new netmask
+ * @param gw the new default gateway
+ */
+void
+netif_set_addr(struct netif *netif, const ip4_addr_t *ipaddr, const ip4_addr_t *netmask,
+ const ip4_addr_t *gw)
+{
+ if (ip4_addr_isany(ipaddr)) {
+ /* when removing an address, we have to remove it *before* changing netmask/gw
+ to ensure that tcp RST segment can be sent correctly */
+ netif_set_ipaddr(netif, ipaddr);
+ netif_set_netmask(netif, netmask);
+ netif_set_gw(netif, gw);
+ } else {
+ netif_set_netmask(netif, netmask);
+ netif_set_gw(netif, gw);
+ /* set ipaddr last to ensure netmask/gw have been set when status callback is called */
+ netif_set_ipaddr(netif, ipaddr);
+ }
+}
+#endif /* LWIP_IPV4*/
+
+/**
+ * @ingroup netif
+ * Remove a network interface from the list of lwIP netifs.
+ *
+ * @param netif the network interface to remove
+ */
+void
+netif_remove(struct netif *netif)
+{
+#if LWIP_IPV6
+ int i;
+#endif
+
+ if (netif == NULL) {
+ return;
+ }
+
+#if LWIP_IPV4
+ if (!ip4_addr_isany_val(*netif_ip4_addr(netif))) {
+#if LWIP_TCP
+ tcp_netif_ip_addr_changed(netif_ip_addr4(netif), NULL);
+#endif /* LWIP_TCP */
+#if LWIP_UDP
+ udp_netif_ip_addr_changed(netif_ip_addr4(netif), NULL);
+#endif /* LWIP_UDP */
+#if LWIP_RAW
+ raw_netif_ip_addr_changed(netif_ip_addr4(netif), NULL);
+#endif /* LWIP_RAW */
+ }
+
+#if LWIP_IGMP
+ /* stop IGMP processing */
+ if (netif->flags & NETIF_FLAG_IGMP) {
+ igmp_stop(netif);
+ }
+#endif /* LWIP_IGMP */
+#endif /* LWIP_IPV4*/
+
+#if LWIP_IPV6
+ for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+ if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i))) {
+#if LWIP_TCP
+ tcp_netif_ip_addr_changed(netif_ip_addr6(netif, i), NULL);
+#endif /* LWIP_TCP */
+#if LWIP_UDP
+ udp_netif_ip_addr_changed(netif_ip_addr6(netif, i), NULL);
+#endif /* LWIP_UDP */
+#if LWIP_RAW
+ raw_netif_ip_addr_changed(netif_ip_addr6(netif, i), NULL);
+#endif /* LWIP_RAW */
+ }
+ }
+#if LWIP_IPV6_MLD
+ /* stop MLD processing */
+ mld6_stop(netif);
+#endif /* LWIP_IPV6_MLD */
+#endif /* LWIP_IPV6 */
+ if (netif_is_up(netif)) {
+ /* set netif down before removing (call callback function) */
+ netif_set_down(netif);
+ }
+
+ mib2_remove_ip4(netif);
+
+ /* this netif is default? */
+ if (netif_default == netif) {
+ /* reset default netif */
+ netif_set_default(NULL);
+ }
+ /* is it the first netif? */
+ if (netif_list == netif) {
+ netif_list = netif->next;
+ } else {
+ /* look for netif further down the list */
+ struct netif * tmp_netif;
+ for (tmp_netif = netif_list; tmp_netif != NULL; tmp_netif = tmp_netif->next) {
+ if (tmp_netif->next == netif) {
+ tmp_netif->next = netif->next;
+ break;
+ }
+ }
+ if (tmp_netif == NULL) {
+ return; /* netif is not on the list */
+ }
+ }
+ mib2_netif_removed(netif);
+#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") );
+}
+
+/**
+ * @ingroup netif
+ * Find a network interface by searching for its name
+ *
+ * @param name the name of the netif (like netif->name) plus concatenated number
+ * in ascii representation (e.g. 'en0')
+ */
+struct netif *
+netif_find(const char *name)
+{
+ struct netif *netif;
+ u8_t num;
+
+ if (name == NULL) {
+ return NULL;
+ }
+
+ num = (u8_t)(name[2] - '0');
+
+ for (netif = netif_list; netif != NULL; netif = netif->next) {
+ if (num == netif->num &&
+ name[0] == netif->name[0] &&
+ name[1] == netif->name[1]) {
+ LWIP_DEBUGF(NETIF_DEBUG, ("netif_find: found %c%c\n", name[0], name[1]));
+ return netif;
+ }
+ }
+ LWIP_DEBUGF(NETIF_DEBUG, ("netif_find: didn't find %c%c\n", name[0], name[1]));
+ return NULL;
+}
+
+#if LWIP_IPV4
+/**
+ * @ingroup netif_ip4
+ * Change the IP address of a network interface
+ *
+ * @param netif the network interface to change
+ * @param ipaddr the new IP address
+ *
+ * @note call netif_set_addr() if you also want to change netmask and
+ * default gateway
+ */
+void
+netif_set_ipaddr(struct netif *netif, const ip4_addr_t *ipaddr)
+{
+ ip_addr_t new_addr;
+ *ip_2_ip4(&new_addr) = (ipaddr ? *ipaddr : *IP4_ADDR_ANY4);
+ IP_SET_TYPE_VAL(new_addr, IPADDR_TYPE_V4);
+
+ /* address is actually being changed? */
+ if (ip4_addr_cmp(ip_2_ip4(&new_addr), netif_ip4_addr(netif)) == 0) {
+ LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_STATE, ("netif_set_ipaddr: netif address being changed\n"));
+#if LWIP_TCP
+ tcp_netif_ip_addr_changed(netif_ip_addr4(netif), &new_addr);
+#endif /* LWIP_TCP */
+#if LWIP_UDP
+ udp_netif_ip_addr_changed(netif_ip_addr4(netif), &new_addr);
+#endif /* LWIP_UDP */
+#if LWIP_RAW
+ raw_netif_ip_addr_changed(netif_ip_addr4(netif), &new_addr);
+#endif /* LWIP_RAW */
+
+ mib2_remove_ip4(netif);
+ mib2_remove_route_ip4(0, netif);
+ /* set new IP address to netif */
+ ip4_addr_set(ip_2_ip4(&netif->ip_addr), ipaddr);
+ IP_SET_TYPE_VAL(netif->ip_addr, IPADDR_TYPE_V4);
+ mib2_add_ip4(netif);
+ mib2_add_route_ip4(0, netif);
+
+ netif_issue_reports(netif, NETIF_REPORT_TYPE_IPV4);
+
+ NETIF_STATUS_CALLBACK(netif);
+ }
+
+ LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("netif: IP address of interface %c%c set to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ netif->name[0], netif->name[1],
+ ip4_addr1_16(netif_ip4_addr(netif)),
+ ip4_addr2_16(netif_ip4_addr(netif)),
+ ip4_addr3_16(netif_ip4_addr(netif)),
+ ip4_addr4_16(netif_ip4_addr(netif))));
+}
+
+/**
+ * @ingroup netif_ip4
+ * Change the default gateway for a network interface
+ *
+ * @param netif the network interface to change
+ * @param gw the new default gateway
+ *
+ * @note call netif_set_addr() if you also want to change ip address and netmask
+ */
+void
+netif_set_gw(struct netif *netif, const ip4_addr_t *gw)
+{
+ ip4_addr_set(ip_2_ip4(&netif->gw), gw);
+ IP_SET_TYPE_VAL(netif->gw, IPADDR_TYPE_V4);
+ LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("netif: GW address of interface %c%c set to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ netif->name[0], netif->name[1],
+ ip4_addr1_16(netif_ip4_gw(netif)),
+ ip4_addr2_16(netif_ip4_gw(netif)),
+ ip4_addr3_16(netif_ip4_gw(netif)),
+ ip4_addr4_16(netif_ip4_gw(netif))));
+}
+
+/**
+ * @ingroup netif_ip4
+ * Change the netmask of a network interface
+ *
+ * @param netif the network interface to change
+ * @param netmask the new netmask
+ *
+ * @note call netif_set_addr() if you also want to change ip address and
+ * default gateway
+ */
+void
+netif_set_netmask(struct netif *netif, const ip4_addr_t *netmask)
+{
+ mib2_remove_route_ip4(0, netif);
+ /* set new netmask to netif */
+ ip4_addr_set(ip_2_ip4(&netif->netmask), netmask);
+ IP_SET_TYPE_VAL(netif->netmask, IPADDR_TYPE_V4);
+ mib2_add_route_ip4(0, netif);
+ LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("netif: netmask of interface %c%c set to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ netif->name[0], netif->name[1],
+ ip4_addr1_16(netif_ip4_netmask(netif)),
+ ip4_addr2_16(netif_ip4_netmask(netif)),
+ ip4_addr3_16(netif_ip4_netmask(netif)),
+ ip4_addr4_16(netif_ip4_netmask(netif))));
+}
+#endif /* LWIP_IPV4 */
+
+/**
+ * @ingroup netif
+ * Set a network interface as the default network interface
+ * (used to output all packets for which no specific route is found)
+ *
+ * @param netif the default network interface
+ */
+void
+netif_set_default(struct netif *netif)
+{
+ if (netif == NULL) {
+ /* remove default route */
+ mib2_remove_route_ip4(1, netif);
+ } else {
+ /* install default route */
+ mib2_add_route_ip4(1, netif);
+ }
+ netif_default = netif;
+ LWIP_DEBUGF(NETIF_DEBUG, ("netif: setting default interface %c%c\n",
+ netif ? netif->name[0] : '\'', netif ? netif->name[1] : '\''));
+}
+
+/**
+ * @ingroup netif
+ * Bring an interface up, available for processing
+ * traffic.
+ */
+void
+netif_set_up(struct netif *netif)
+{
+ if (!(netif->flags & NETIF_FLAG_UP)) {
+ netif->flags |= NETIF_FLAG_UP;
+
+ MIB2_COPY_SYSUPTIME_TO(&netif->ts);
+
+ NETIF_STATUS_CALLBACK(netif);
+
+ if (netif->flags & NETIF_FLAG_LINK_UP) {
+ netif_issue_reports(netif, NETIF_REPORT_TYPE_IPV4|NETIF_REPORT_TYPE_IPV6);
+ }
+ }
+}
+
+/** Send ARP/IGMP/MLD/RS events, e.g. on link-up/netif-up or addr-change
+ */
+static void
+netif_issue_reports(struct netif* netif, u8_t report_type)
+{
+#if LWIP_IPV4
+ if ((report_type & NETIF_REPORT_TYPE_IPV4) &&
+ !ip4_addr_isany_val(*netif_ip4_addr(netif))) {
+#if LWIP_ARP
+ /* For Ethernet network interfaces, we would like to send a "gratuitous ARP" */
+ if (netif->flags & (NETIF_FLAG_ETHARP)) {
+ etharp_gratuitous(netif);
+ }
+#endif /* LWIP_ARP */
+
+#if LWIP_IGMP
+ /* resend IGMP memberships */
+ if (netif->flags & NETIF_FLAG_IGMP) {
+ igmp_report_groups(netif);
+ }
+#endif /* LWIP_IGMP */
+ }
+#endif /* LWIP_IPV4 */
+
+#if LWIP_IPV6
+ if (report_type & NETIF_REPORT_TYPE_IPV6) {
+#if LWIP_IPV6_MLD
+ /* send mld memberships */
+ mld6_report_groups(netif);
+#endif /* LWIP_IPV6_MLD */
+#if LWIP_IPV6_SEND_ROUTER_SOLICIT
+ /* Send Router Solicitation messages. */
+ netif->rs_count = LWIP_ND6_MAX_MULTICAST_SOLICIT;
+#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */
+ }
+#endif /* LWIP_IPV6 */
+}
+
+/**
+ * @ingroup netif
+ * Bring an interface down, disabling any traffic processing.
+ */
+void
+netif_set_down(struct netif *netif)
+{
+ if (netif->flags & NETIF_FLAG_UP) {
+ netif->flags &= ~NETIF_FLAG_UP;
+ MIB2_COPY_SYSUPTIME_TO(&netif->ts);
+
+#if LWIP_IPV4 && LWIP_ARP
+ if (netif->flags & NETIF_FLAG_ETHARP) {
+ etharp_cleanup_netif(netif);
+ }
+#endif /* LWIP_IPV4 && LWIP_ARP */
+
+#if LWIP_IPV6
+ nd6_cleanup_netif(netif);
+#endif /* LWIP_IPV6 */
+
+ NETIF_STATUS_CALLBACK(netif);
+ }
+}
+
+#if LWIP_NETIF_STATUS_CALLBACK
+/**
+ * @ingroup netif
+ * Set callback to be called when interface is brought up/down or address is changed while up
+ */
+void
+netif_set_status_callback(struct netif *netif, netif_status_callback_fn status_callback)
+{
+ if (netif) {
+ netif->status_callback = status_callback;
+ }
+}
+#endif /* LWIP_NETIF_STATUS_CALLBACK */
+
+#if LWIP_NETIF_REMOVE_CALLBACK
+/**
+ * @ingroup netif
+ * 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 */
+
+/**
+ * @ingroup netif
+ * Called by a driver when its link goes up
+ */
+void
+netif_set_link_up(struct netif *netif)
+{
+ if (!(netif->flags & NETIF_FLAG_LINK_UP)) {
+ netif->flags |= NETIF_FLAG_LINK_UP;
+
+#if LWIP_DHCP
+ dhcp_network_changed(netif);
+#endif /* LWIP_DHCP */
+
+#if LWIP_AUTOIP
+ autoip_network_changed(netif);
+#endif /* LWIP_AUTOIP */
+
+ if (netif->flags & NETIF_FLAG_UP) {
+ netif_issue_reports(netif, NETIF_REPORT_TYPE_IPV4|NETIF_REPORT_TYPE_IPV6);
+ }
+ NETIF_LINK_CALLBACK(netif);
+ }
+}
+
+/**
+ * @ingroup netif
+ * Called by a driver when its link goes down
+ */
+void
+netif_set_link_down(struct netif *netif )
+{
+ if (netif->flags & NETIF_FLAG_LINK_UP) {
+ netif->flags &= ~NETIF_FLAG_LINK_UP;
+ NETIF_LINK_CALLBACK(netif);
+ }
+}
+
+#if LWIP_NETIF_LINK_CALLBACK
+/**
+ * @ingroup netif
+ * Set callback to be called when link is brought up/down
+ */
+void
+netif_set_link_callback(struct netif *netif, netif_status_callback_fn link_callback)
+{
+ if (netif) {
+ netif->link_callback = link_callback;
+ }
+}
+#endif /* LWIP_NETIF_LINK_CALLBACK */
+
+#if ENABLE_LOOPBACK
+/**
+ * @ingroup netif
+ * Send an IP packet to be received on the same netif (loopif-like).
+ * The pbuf is simply copied and handed back to netif->input.
+ * In multithreaded mode, this is done directly since netif->input must put
+ * the packet on a queue.
+ * In callback mode, the packet is put on an internal queue and is fed to
+ * netif->input by netif_poll().
+ *
+ * @param netif the lwip network interface structure
+ * @param p the (IP) packet to 'send'
+ * @return ERR_OK if the packet has been sent
+ * ERR_MEM if the pbuf used to copy the packet couldn't be allocated
+ */
+err_t
+netif_loop_output(struct netif *netif, struct pbuf *p)
+{
+ struct pbuf *r;
+ err_t err;
+ struct pbuf *last;
+#if LWIP_LOOPBACK_MAX_PBUFS
+ u16_t clen = 0;
+#endif /* LWIP_LOOPBACK_MAX_PBUFS */
+ /* If we have a loopif, SNMP counters are adjusted for it,
+ * if not they are adjusted for 'netif'. */
+#if MIB2_STATS
+#if LWIP_HAVE_LOOPIF
+ struct netif *stats_if = &loop_netif;
+#else /* LWIP_HAVE_LOOPIF */
+ struct netif *stats_if = netif;
+#endif /* LWIP_HAVE_LOOPIF */
+#endif /* MIB2_STATS */
+ SYS_ARCH_DECL_PROTECT(lev);
+
+ /* Allocate a new pbuf */
+ r = pbuf_alloc(PBUF_LINK, p->tot_len, PBUF_RAM);
+ if (r == NULL) {
+ LINK_STATS_INC(link.memerr);
+ LINK_STATS_INC(link.drop);
+ MIB2_STATS_NETIF_INC(stats_if, ifoutdiscards);
+ return ERR_MEM;
+ }
+#if LWIP_LOOPBACK_MAX_PBUFS
+ clen = pbuf_clen(r);
+ /* check for overflow or too many pbuf on queue */
+ if (((netif->loop_cnt_current + clen) < netif->loop_cnt_current) ||
+ ((netif->loop_cnt_current + clen) > LWIP_LOOPBACK_MAX_PBUFS)) {
+ pbuf_free(r);
+ LINK_STATS_INC(link.memerr);
+ LINK_STATS_INC(link.drop);
+ MIB2_STATS_NETIF_INC(stats_if, ifoutdiscards);
+ return ERR_MEM;
+ }
+ netif->loop_cnt_current += clen;
+#endif /* LWIP_LOOPBACK_MAX_PBUFS */
+
+ /* Copy the whole pbuf queue p into the single pbuf r */
+ if ((err = pbuf_copy(r, p)) != ERR_OK) {
+ pbuf_free(r);
+ LINK_STATS_INC(link.memerr);
+ LINK_STATS_INC(link.drop);
+ MIB2_STATS_NETIF_INC(stats_if, ifoutdiscards);
+ return err;
+ }
+
+ /* Put the packet on a linked list which gets emptied through calling
+ netif_poll(). */
+
+ /* let last point to the last pbuf in chain r */
+ for (last = r; last->next != NULL; last = last->next);
+
+ SYS_ARCH_PROTECT(lev);
+ if (netif->loop_first != NULL) {
+ LWIP_ASSERT("if first != NULL, last must also be != NULL", netif->loop_last != NULL);
+ netif->loop_last->next = r;
+ netif->loop_last = last;
+ } else {
+ netif->loop_first = r;
+ netif->loop_last = last;
+ }
+ SYS_ARCH_UNPROTECT(lev);
+
+ LINK_STATS_INC(link.xmit);
+ MIB2_STATS_NETIF_ADD(stats_if, ifoutoctets, p->tot_len);
+ MIB2_STATS_NETIF_INC(stats_if, ifoutucastpkts);
+
+#if LWIP_NETIF_LOOPBACK_MULTITHREADING
+ /* For multithreading environment, schedule a call to netif_poll */
+ tcpip_callback_with_block((tcpip_callback_fn)netif_poll, netif, 0);
+#endif /* LWIP_NETIF_LOOPBACK_MULTITHREADING */
+
+ return ERR_OK;
+}
+
+#if LWIP_HAVE_LOOPIF
+#if LWIP_IPV4
+static err_t
+netif_loop_output_ipv4(struct netif *netif, struct pbuf *p, const ip4_addr_t* addr)
+{
+ LWIP_UNUSED_ARG(addr);
+ return netif_loop_output(netif, p);
+}
+#endif /* LWIP_IPV4 */
+
+#if LWIP_IPV6
+static err_t
+netif_loop_output_ipv6(struct netif *netif, struct pbuf *p, const ip6_addr_t* addr)
+{
+ LWIP_UNUSED_ARG(addr);
+ return netif_loop_output(netif, p);
+}
+#endif /* LWIP_IPV6 */
+#endif /* LWIP_HAVE_LOOPIF */
+
+
+/**
+ * Call netif_poll() in the main loop of your application. This is to prevent
+ * reentering non-reentrant functions like tcp_input(). Packets passed to
+ * netif_loop_output() are put on a list that is passed to netif->input() by
+ * netif_poll().
+ */
+void
+netif_poll(struct netif *netif)
+{
+ /* If we have a loopif, SNMP counters are adjusted for it,
+ * if not they are adjusted for 'netif'. */
+#if MIB2_STATS
+#if LWIP_HAVE_LOOPIF
+ struct netif *stats_if = &loop_netif;
+#else /* LWIP_HAVE_LOOPIF */
+ struct netif *stats_if = netif;
+#endif /* LWIP_HAVE_LOOPIF */
+#endif /* MIB2_STATS */
+ SYS_ARCH_DECL_PROTECT(lev);
+
+ /* Get a packet from the list. With SYS_LIGHTWEIGHT_PROT=1, this is protected */
+ SYS_ARCH_PROTECT(lev);
+ while (netif->loop_first != NULL) {
+ struct pbuf *in, *in_end;
+#if LWIP_LOOPBACK_MAX_PBUFS
+ u8_t clen = 1;
+#endif /* LWIP_LOOPBACK_MAX_PBUFS */
+
+ in = in_end = netif->loop_first;
+ while (in_end->len != in_end->tot_len) {
+ LWIP_ASSERT("bogus pbuf: len != tot_len but next == NULL!", in_end->next != NULL);
+ in_end = in_end->next;
+#if LWIP_LOOPBACK_MAX_PBUFS
+ clen++;
+#endif /* LWIP_LOOPBACK_MAX_PBUFS */
+ }
+#if LWIP_LOOPBACK_MAX_PBUFS
+ /* adjust the number of pbufs on queue */
+ LWIP_ASSERT("netif->loop_cnt_current underflow",
+ ((netif->loop_cnt_current - clen) < netif->loop_cnt_current));
+ netif->loop_cnt_current -= clen;
+#endif /* LWIP_LOOPBACK_MAX_PBUFS */
+
+ /* 'in_end' now points to the last pbuf from 'in' */
+ if (in_end == netif->loop_last) {
+ /* this was the last pbuf in the list */
+ netif->loop_first = netif->loop_last = NULL;
+ } else {
+ /* pop the pbuf off the list */
+ netif->loop_first = in_end->next;
+ LWIP_ASSERT("should not be null since first != last!", netif->loop_first != NULL);
+ }
+ /* De-queue the pbuf from its successors on the 'loop_' list. */
+ in_end->next = NULL;
+ SYS_ARCH_UNPROTECT(lev);
+
+ LINK_STATS_INC(link.recv);
+ MIB2_STATS_NETIF_ADD(stats_if, ifinoctets, in->tot_len);
+ MIB2_STATS_NETIF_INC(stats_if, ifinucastpkts);
+ /* loopback packets are always IP packets! */
+ if (ip_input(in, netif) != ERR_OK) {
+ pbuf_free(in);
+ }
+ SYS_ARCH_PROTECT(lev);
+ }
+ SYS_ARCH_UNPROTECT(lev);
+}
+
+#if !LWIP_NETIF_LOOPBACK_MULTITHREADING
+/**
+ * Calls netif_poll() for every netif on the netif_list.
+ */
+void
+netif_poll_all(void)
+{
+ struct netif *netif = netif_list;
+ /* loop through netifs */
+ while (netif != NULL) {
+ netif_poll(netif);
+ /* proceed to next network interface */
+ netif = netif->next;
+ }
+}
+#endif /* !LWIP_NETIF_LOOPBACK_MULTITHREADING */
+#endif /* ENABLE_LOOPBACK */
+
+#if LWIP_NUM_NETIF_CLIENT_DATA > 0
+/**
+ * @ingroup netif_cd
+ * Allocate an index to store data in client_data member of struct netif.
+ * Returned value is an index in mentioned array.
+ * @see LWIP_NUM_NETIF_CLIENT_DATA
+ */
+u8_t
+netif_alloc_client_data_id(void)
+{
+ u8_t result = netif_client_id;
+ netif_client_id++;
+
+ LWIP_ASSERT("Increase LWIP_NUM_NETIF_CLIENT_DATA in lwipopts.h", result < LWIP_NUM_NETIF_CLIENT_DATA);
+ return result + LWIP_NETIF_CLIENT_DATA_INDEX_MAX;
+}
+#endif
+
+#if LWIP_IPV6
+/**
+ * @ingroup netif_ip6
+ * Change an IPv6 address of a network interface
+ *
+ * @param netif the network interface to change
+ * @param addr_idx index of the IPv6 address
+ * @param addr6 the new IPv6 address
+ *
+ * @note call netif_ip6_addr_set_state() to set the address valid/temptative
+ */
+void
+netif_ip6_addr_set(struct netif *netif, s8_t addr_idx, const ip6_addr_t *addr6)
+{
+ LWIP_ASSERT("addr6 != NULL", addr6 != NULL);
+ netif_ip6_addr_set_parts(netif, addr_idx, addr6->addr[0], addr6->addr[1],
+ addr6->addr[2], addr6->addr[3]);
+}
+
+/*
+ * Change an IPv6 address of a network interface (internal version taking 4 * u32_t)
+ *
+ * @param netif the network interface to change
+ * @param addr_idx index of the IPv6 address
+ * @param i0 word0 of the new IPv6 address
+ * @param i1 word1 of the new IPv6 address
+ * @param i2 word2 of the new IPv6 address
+ * @param i3 word3 of the new IPv6 address
+ */
+void
+netif_ip6_addr_set_parts(struct netif *netif, s8_t addr_idx, u32_t i0, u32_t i1, u32_t i2, u32_t i3)
+{
+ const ip6_addr_t *old_addr;
+ LWIP_ASSERT("netif != NULL", netif != NULL);
+ LWIP_ASSERT("invalid index", addr_idx < LWIP_IPV6_NUM_ADDRESSES);
+
+ old_addr = netif_ip6_addr(netif, addr_idx);
+ /* address is actually being changed? */
+ if ((old_addr->addr[0] != i0) || (old_addr->addr[1] != i1) ||
+ (old_addr->addr[2] != i2) || (old_addr->addr[3] != i3)) {
+ LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_STATE, ("netif_ip6_addr_set: netif address being changed\n"));
+
+ if (netif_ip6_addr_state(netif, addr_idx) & IP6_ADDR_VALID) {
+#if LWIP_TCP || LWIP_UDP
+ ip_addr_t new_ipaddr;
+ IP_ADDR6(&new_ipaddr, i0, i1, i2, i3);
+#endif /* LWIP_TCP || LWIP_UDP */
+#if LWIP_TCP
+ tcp_netif_ip_addr_changed(netif_ip_addr6(netif, addr_idx), &new_ipaddr);
+#endif /* LWIP_TCP */
+#if LWIP_UDP
+ udp_netif_ip_addr_changed(netif_ip_addr6(netif, addr_idx), &new_ipaddr);
+#endif /* LWIP_UDP */
+#if LWIP_RAW
+ raw_netif_ip_addr_changed(netif_ip_addr6(netif, addr_idx), &new_ipaddr);
+#endif /* LWIP_RAW */
+ }
+ /* @todo: remove/readd mib2 ip6 entries? */
+
+ IP6_ADDR(ip_2_ip6(&(netif->ip6_addr[addr_idx])), i0, i1, i2, i3);
+ IP_SET_TYPE_VAL(netif->ip6_addr[addr_idx], IPADDR_TYPE_V6);
+
+ if (netif_ip6_addr_state(netif, addr_idx) & IP6_ADDR_VALID) {
+ netif_issue_reports(netif, NETIF_REPORT_TYPE_IPV6);
+ NETIF_STATUS_CALLBACK(netif);
+ }
+ }
+
+ LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("netif: IPv6 address %d of interface %c%c set to %s/0x%"X8_F"\n",
+ addr_idx, netif->name[0], netif->name[1], ip6addr_ntoa(netif_ip6_addr(netif, addr_idx)),
+ netif_ip6_addr_state(netif, addr_idx)));
+}
+
+/**
+ * @ingroup netif_ip6
+ * Change the state of an IPv6 address of a network interface
+ * (INVALID, TEMPTATIVE, PREFERRED, DEPRECATED, where TEMPTATIVE
+ * includes the number of checks done, see ip6_addr.h)
+ *
+ * @param netif the network interface to change
+ * @param addr_idx index of the IPv6 address
+ * @param state the new IPv6 address state
+ */
+void
+netif_ip6_addr_set_state(struct netif* netif, s8_t addr_idx, u8_t state)
+{
+ u8_t old_state;
+ LWIP_ASSERT("netif != NULL", netif != NULL);
+ LWIP_ASSERT("invalid index", addr_idx < LWIP_IPV6_NUM_ADDRESSES);
+
+ old_state = netif_ip6_addr_state(netif, addr_idx);
+ /* state is actually being changed? */
+ if (old_state != state) {
+ u8_t old_valid = old_state & IP6_ADDR_VALID;
+ u8_t new_valid = state & IP6_ADDR_VALID;
+ LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_STATE, ("netif_ip6_addr_set_state: netif address state being changed\n"));
+
+#if LWIP_IPV6_MLD
+ /* Reevaluate solicited-node multicast group membership. */
+ if (netif->flags & NETIF_FLAG_MLD6) {
+ nd6_adjust_mld_membership(netif, addr_idx, state);
+ }
+#endif /* LWIP_IPV6_MLD */
+
+ if (old_valid && !new_valid) {
+ /* address about to be removed by setting invalid */
+#if LWIP_TCP
+ tcp_netif_ip_addr_changed(netif_ip_addr6(netif, addr_idx), NULL);
+#endif /* LWIP_TCP */
+#if LWIP_UDP
+ udp_netif_ip_addr_changed(netif_ip_addr6(netif, addr_idx), NULL);
+#endif /* LWIP_UDP */
+#if LWIP_RAW
+ raw_netif_ip_addr_changed(netif_ip_addr6(netif, addr_idx), NULL);
+#endif /* LWIP_RAW */
+ /* @todo: remove mib2 ip6 entries? */
+ }
+ netif->ip6_addr_state[addr_idx] = state;
+
+ if (!old_valid && new_valid) {
+ /* address added by setting valid */
+ /* @todo: add mib2 ip6 entries? */
+ netif_issue_reports(netif, NETIF_REPORT_TYPE_IPV6);
+ }
+ if ((old_state & IP6_ADDR_PREFERRED) != (state & IP6_ADDR_PREFERRED)) {
+ /* address state has changed (valid flag changed or switched between
+ preferred and deprecated) -> call the callback function */
+ NETIF_STATUS_CALLBACK(netif);
+ }
+ }
+
+ LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("netif: IPv6 address %d of interface %c%c set to %s/0x%"X8_F"\n",
+ addr_idx, netif->name[0], netif->name[1], ip6addr_ntoa(netif_ip6_addr(netif, addr_idx)),
+ netif_ip6_addr_state(netif, addr_idx)));
+}
+
+/**
+ * Checks if a specific address is assigned to the netif and returns its
+ * index.
+ *
+ * @param netif the netif to check
+ * @param ip6addr the IPv6 address to find
+ * @return >= 0: address found, this is its index
+ * -1: address not found on this netif
+ */
+s8_t
+netif_get_ip6_addr_match(struct netif *netif, const ip6_addr_t *ip6addr)
+{
+ s8_t i;
+ for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+ if (!ip6_addr_isinvalid(netif_ip6_addr_state(netif, i)) &&
+ ip6_addr_cmp(netif_ip6_addr(netif, i), ip6addr)) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+/**
+ * @ingroup netif_ip6
+ * Create a link-local IPv6 address on a netif (stored in slot 0)
+ *
+ * @param netif the netif to create the address on
+ * @param from_mac_48bit if != 0, assume hwadr is a 48-bit MAC address (std conversion)
+ * if == 0, use hwaddr directly as interface ID
+ */
+void
+netif_create_ip6_linklocal_address(struct netif *netif, u8_t from_mac_48bit)
+{
+ u8_t i, addr_index;
+
+ /* Link-local prefix. */
+ ip_2_ip6(&netif->ip6_addr[0])->addr[0] = PP_HTONL(0xfe800000ul);
+ ip_2_ip6(&netif->ip6_addr[0])->addr[1] = 0;
+
+ /* Generate interface ID. */
+ if (from_mac_48bit) {
+ /* Assume hwaddr is a 48-bit IEEE 802 MAC. Convert to EUI-64 address. Complement Group bit. */
+ ip_2_ip6(&netif->ip6_addr[0])->addr[2] = lwip_htonl((((u32_t)(netif->hwaddr[0] ^ 0x02)) << 24) |
+ ((u32_t)(netif->hwaddr[1]) << 16) |
+ ((u32_t)(netif->hwaddr[2]) << 8) |
+ (0xff));
+ ip_2_ip6(&netif->ip6_addr[0])->addr[3] = lwip_htonl((0xfeul << 24) |
+ ((u32_t)(netif->hwaddr[3]) << 16) |
+ ((u32_t)(netif->hwaddr[4]) << 8) |
+ (netif->hwaddr[5]));
+ } else {
+ /* Use hwaddr directly as interface ID. */
+ ip_2_ip6(&netif->ip6_addr[0])->addr[2] = 0;
+ ip_2_ip6(&netif->ip6_addr[0])->addr[3] = 0;
+
+ addr_index = 3;
+ for (i = 0; (i < 8) && (i < netif->hwaddr_len); i++) {
+ if (i == 4) {
+ addr_index--;
+ }
+ ip_2_ip6(&netif->ip6_addr[0])->addr[addr_index] |= ((u32_t)(netif->hwaddr[netif->hwaddr_len - i - 1])) << (8 * (i & 0x03));
+ }
+ }
+
+ /* Set address state. */
+#if LWIP_IPV6_DUP_DETECT_ATTEMPTS
+ /* Will perform duplicate address detection (DAD). */
+ netif_ip6_addr_set_state(netif, 0, IP6_ADDR_TENTATIVE);
+#else
+ /* Consider address valid. */
+ netif_ip6_addr_set_state(netif, 0, IP6_ADDR_PREFERRED);
+#endif /* LWIP_IPV6_AUTOCONFIG */
+}
+
+/**
+ * @ingroup netif_ip6
+ * This function allows for the easy addition of a new IPv6 address to an interface.
+ * It takes care of finding an empty slot and then sets the address tentative
+ * (to make sure that all the subsequent processing happens).
+ *
+ * @param netif netif to add the address on
+ * @param ip6addr address to add
+ * @param chosen_idx if != NULL, the chosen IPv6 address index will be stored here
+ */
+err_t
+netif_add_ip6_address(struct netif *netif, const ip6_addr_t *ip6addr, s8_t *chosen_idx)
+{
+ s8_t i;
+
+ i = netif_get_ip6_addr_match(netif, ip6addr);
+ if (i >= 0) {
+ /* Address already added */
+ if (chosen_idx != NULL) {
+ *chosen_idx = i;
+ }
+ return ERR_OK;
+ }
+
+ /* Find a free slot -- musn't be the first one (reserved for link local) */
+ for (i = 1; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+ if (ip6_addr_isinvalid(netif_ip6_addr_state(netif, i))) {
+ ip_addr_copy_from_ip6(netif->ip6_addr[i], *ip6addr);
+ netif_ip6_addr_set_state(netif, i, IP6_ADDR_TENTATIVE);
+ if (chosen_idx != NULL) {
+ *chosen_idx = i;
+ }
+ return ERR_OK;
+ }
+ }
+
+ if (chosen_idx != NULL) {
+ *chosen_idx = -1;
+ }
+ return ERR_VAL;
+}
+
+/** Dummy IPv6 output function for netifs not supporting IPv6
+ */
+static err_t
+netif_null_output_ip6(struct netif *netif, struct pbuf *p, const ip6_addr_t *ipaddr)
+{
+ LWIP_UNUSED_ARG(netif);
+ LWIP_UNUSED_ARG(p);
+ LWIP_UNUSED_ARG(ipaddr);
+
+ return ERR_IF;
+}
+#endif /* LWIP_IPV6 */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/pbuf.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/pbuf.c
new file mode 100644
index 0000000..059f83a
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/pbuf.c
@@ -0,0 +1,1442 @@
+/**
+ * @file
+ * Packet buffer management
+ */
+
+/**
+ * @defgroup pbuf Packet buffers (PBUF)
+ * @ingroup infrastructure
+ *
+ * Packets are built from the pbuf data structure. It supports dynamic
+ * memory allocation for packet contents or can reference externally
+ * managed packet contents both in RAM and ROM. Quick allocation for
+ * incoming packets is provided through pools with fixed sized pbufs.
+ *
+ * A packet may span over multiple pbufs, chained as a singly linked
+ * list. This is called a "pbuf chain".
+ *
+ * Multiple packets may be queued, also using this singly linked list.
+ * This is called a "packet queue".
+ *
+ * So, a packet queue consists of one or more pbuf chains, each of
+ * which consist of one or more pbufs. CURRENTLY, PACKET QUEUES ARE
+ * NOT SUPPORTED!!! Use helper structs to queue multiple packets.
+ *
+ * The differences between a pbuf chain and a packet queue are very
+ * precise but subtle.
+ *
+ * The last pbuf of a packet has a ->tot_len field that equals the
+ * ->len field. It can be found by traversing the list. If the last
+ * pbuf of a packet has a ->next field other than NULL, more packets
+ * are on the queue.
+ *
+ * Therefore, looping through a pbuf of a single packet, has an
+ * loop end condition (tot_len == p->len), NOT (next == NULL).
+ *
+ * Example of custom pbuf usage for zero-copy RX:
+ @code{.c}
+typedef struct my_custom_pbuf
+{
+ struct pbuf_custom p;
+ void* dma_descriptor;
+} my_custom_pbuf_t;
+
+LWIP_MEMPOOL_DECLARE(RX_POOL, 10, sizeof(my_custom_pbuf_t), "Zero-copy RX PBUF pool");
+
+void my_pbuf_free_custom(void* p)
+{
+ my_custom_pbuf_t* my_puf = (my_custom_pbuf_t*)p;
+
+ LOCK_INTERRUPTS();
+ free_rx_dma_descriptor(my_pbuf->dma_descriptor);
+ LWIP_MEMPOOL_FREE(RX_POOL, my_pbuf);
+ UNLOCK_INTERRUPTS();
+}
+
+void eth_rx_irq()
+{
+ dma_descriptor* dma_desc = get_RX_DMA_descriptor_from_ethernet();
+ my_custom_pbuf_t* my_pbuf = (my_custom_pbuf_t*)LWIP_MEMPOOL_ALLOC(RX_POOL);
+
+ my_pbuf->p.custom_free_function = my_pbuf_free_custom;
+ my_pbuf->dma_descriptor = dma_desc;
+
+ invalidate_cpu_cache(dma_desc->rx_data, dma_desc->rx_length);
+
+ struct pbuf* p = pbuf_alloced_custom(PBUF_RAW,
+ dma_desc->rx_length,
+ PBUF_REF,
+ &my_pbuf->p,
+ dma_desc->rx_data,
+ dma_desc->max_buffer_size);
+
+ if(netif->input(p, netif) != ERR_OK) {
+ pbuf_free(p);
+ }
+}
+ @endcode
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#include "lwip/stats.h"
+#include "lwip/def.h"
+#include "lwip/mem.h"
+#include "lwip/memp.h"
+#include "lwip/pbuf.h"
+#include "lwip/sys.h"
+#if LWIP_TCP && TCP_QUEUE_OOSEQ
+#include "lwip/priv/tcp_priv.h"
+#endif
+#if LWIP_CHECKSUM_ON_COPY
+#include "lwip/inet_chksum.h"
+#endif
+
+#include <string.h>
+
+#define SIZEOF_STRUCT_PBUF LWIP_MEM_ALIGN_SIZE(sizeof(struct pbuf))
+/* Since the pool is created in memp, PBUF_POOL_BUFSIZE will be automatically
+ aligned there. Therefore, PBUF_POOL_BUFSIZE_ALIGNED can be used here. */
+#define PBUF_POOL_BUFSIZE_ALIGNED LWIP_MEM_ALIGN_SIZE(PBUF_POOL_BUFSIZE)
+
+#if !LWIP_TCP || !TCP_QUEUE_OOSEQ || !PBUF_POOL_FREE_OOSEQ
+#define PBUF_POOL_IS_EMPTY()
+#else /* !LWIP_TCP || !TCP_QUEUE_OOSEQ || !PBUF_POOL_FREE_OOSEQ */
+
+#if !NO_SYS
+#ifndef PBUF_POOL_FREE_OOSEQ_QUEUE_CALL
+#include "lwip/tcpip.h"
+#define PBUF_POOL_FREE_OOSEQ_QUEUE_CALL() do { \
+ if (tcpip_callback_with_block(pbuf_free_ooseq_callback, NULL, 0) != ERR_OK) { \
+ SYS_ARCH_PROTECT(old_level); \
+ pbuf_free_ooseq_pending = 0; \
+ SYS_ARCH_UNPROTECT(old_level); \
+ } } while(0)
+#endif /* PBUF_POOL_FREE_OOSEQ_QUEUE_CALL */
+#endif /* !NO_SYS */
+
+volatile u8_t pbuf_free_ooseq_pending;
+#define PBUF_POOL_IS_EMPTY() pbuf_pool_is_empty()
+
+/**
+ * Attempt to reclaim some memory from queued out-of-sequence TCP segments
+ * if we run out of pool pbufs. It's better to give priority to new packets
+ * if we're running out.
+ *
+ * This must be done in the correct thread context therefore this function
+ * can only be used with NO_SYS=0 and through tcpip_callback.
+ */
+#if !NO_SYS
+static
+#endif /* !NO_SYS */
+void
+pbuf_free_ooseq(void)
+{
+ struct tcp_pcb* pcb;
+ SYS_ARCH_SET(pbuf_free_ooseq_pending, 0);
+
+ for (pcb = tcp_active_pcbs; NULL != pcb; pcb = pcb->next) {
+ if (NULL != pcb->ooseq) {
+ /** Free the ooseq pbufs of one PCB only */
+ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_free_ooseq: freeing out-of-sequence pbufs\n"));
+ tcp_segs_free(pcb->ooseq);
+ pcb->ooseq = NULL;
+ return;
+ }
+ }
+}
+
+#if !NO_SYS
+/**
+ * Just a callback function for tcpip_callback() 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_SET(pbuf_free_ooseq_pending, 1);
+#else /* PBUF_POOL_FREE_OOSEQ_QUEUE_CALL */
+ u8_t queued;
+ SYS_ARCH_DECL_PROTECT(old_level);
+ SYS_ARCH_PROTECT(old_level);
+ queued = pbuf_free_ooseq_pending;
+ pbuf_free_ooseq_pending = 1;
+ SYS_ARCH_UNPROTECT(old_level);
+
+ if (!queued) {
+ /* queue a call to pbuf_free_ooseq if not already queued */
+ PBUF_POOL_FREE_OOSEQ_QUEUE_CALL();
+ }
+#endif /* PBUF_POOL_FREE_OOSEQ_QUEUE_CALL */
+}
+#endif /* !LWIP_TCP || !TCP_QUEUE_OOSEQ || !PBUF_POOL_FREE_OOSEQ */
+
+/**
+ * @ingroup pbuf
+ * Allocates a pbuf of the given type (possibly a chain for PBUF_POOL type).
+ *
+ * The actual memory allocated for the pbuf is determined by the
+ * layer at which the pbuf is allocated and the requested size
+ * (from the size parameter).
+ *
+ * @param layer flag to define header size
+ * @param length size of the pbuf's payload
+ * @param type this parameter decides how and where the pbuf
+ * should be allocated as follows:
+ *
+ * - PBUF_RAM: buffer memory for pbuf is allocated as one large
+ * chunk. This includes protocol headers as well.
+ * - PBUF_ROM: no buffer memory is allocated for the pbuf, even for
+ * protocol headers. Additional headers must be prepended
+ * by allocating another pbuf and chain in to the front of
+ * the ROM pbuf. It is assumed that the memory used is really
+ * similar to ROM in that it is immutable and will not be
+ * changed. Memory which is dynamic should generally not
+ * be attached to PBUF_ROM pbufs. Use PBUF_REF instead.
+ * - PBUF_REF: no buffer memory is allocated for the pbuf, even for
+ * protocol headers. It is assumed that the pbuf is only
+ * being used in a single thread. If the pbuf gets queued,
+ * then pbuf_take should be called to copy the buffer.
+ * - PBUF_POOL: the pbuf is allocated as a pbuf chain, with pbufs from
+ * the pbuf pool that is allocated during pbuf_init().
+ *
+ * @return the allocated pbuf. If multiple pbufs where allocated, this
+ * is the first pbuf of a pbuf chain.
+ */
+struct pbuf *
+pbuf_alloc(pbuf_layer layer, u16_t length, pbuf_type type)
+{
+ struct pbuf *p, *q, *r;
+ u16_t offset;
+ s32_t rem_len; /* remaining length */
+ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc(length=%"U16_F")\n", length));
+
+ /* determine header offset */
+ switch (layer) {
+ case PBUF_TRANSPORT:
+ /* add room for transport (often TCP) layer header */
+ offset = PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN;
+ break;
+ case PBUF_IP:
+ /* add room for IP layer header */
+ offset = PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN + PBUF_IP_HLEN;
+ break;
+ case PBUF_LINK:
+ /* add room for link layer header */
+ offset = PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN;
+ break;
+ case PBUF_RAW_TX:
+ /* add room for encapsulating link layer headers (e.g. 802.11) */
+ offset = PBUF_LINK_ENCAPSULATION_HLEN;
+ break;
+ case PBUF_RAW:
+ /* no offset (e.g. RX buffers or chain successors) */
+ offset = 0;
+ break;
+ default:
+ LWIP_ASSERT("pbuf_alloc: bad pbuf layer", 0);
+ return NULL;
+ }
+
+ switch (type) {
+ case PBUF_POOL:
+ /* allocate head of pbuf chain into p */
+ p = (struct pbuf *)memp_malloc(MEMP_PBUF_POOL);
+ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc: allocated pbuf %p\n", (void *)p));
+ if (p == NULL) {
+ PBUF_POOL_IS_EMPTY();
+ return NULL;
+ }
+ p->type = type;
+ p->next = NULL;
+
+ /* make the payload pointer point 'offset' bytes into pbuf data memory */
+ p->payload = LWIP_MEM_ALIGN((void *)((u8_t *)p + (SIZEOF_STRUCT_PBUF + offset)));
+ LWIP_ASSERT("pbuf_alloc: pbuf p->payload properly aligned",
+ ((mem_ptr_t)p->payload % MEM_ALIGNMENT) == 0);
+ /* the total length of the pbuf chain is the requested size */
+ p->tot_len = length;
+ /* set the length of the first pbuf in the chain */
+ p->len = LWIP_MIN(length, PBUF_POOL_BUFSIZE_ALIGNED - LWIP_MEM_ALIGN_SIZE(offset));
+ LWIP_ASSERT("check p->payload + p->len does not overflow pbuf",
+ ((u8_t*)p->payload + p->len <=
+ (u8_t*)p + SIZEOF_STRUCT_PBUF + PBUF_POOL_BUFSIZE_ALIGNED));
+ LWIP_ASSERT("PBUF_POOL_BUFSIZE must be bigger than MEM_ALIGNMENT",
+ (PBUF_POOL_BUFSIZE_ALIGNED - LWIP_MEM_ALIGN_SIZE(offset)) > 0 );
+ /* set reference count (needed here in case we fail) */
+ p->ref = 1;
+
+ /* now allocate the tail of the pbuf chain */
+
+ /* remember first pbuf for linkage in next iteration */
+ r = p;
+ /* remaining length to be allocated */
+ rem_len = length - p->len;
+ /* any remaining pbufs to be allocated? */
+ while (rem_len > 0) {
+ q = (struct pbuf *)memp_malloc(MEMP_PBUF_POOL);
+ if (q == NULL) {
+ PBUF_POOL_IS_EMPTY();
+ /* free chain so far allocated */
+ pbuf_free(p);
+ /* bail out unsuccessfully */
+ return NULL;
+ }
+ q->type = type;
+ q->flags = 0;
+ q->next = NULL;
+ /* make previous pbuf point to this pbuf */
+ r->next = q;
+ /* set total length of this pbuf and next in chain */
+ LWIP_ASSERT("rem_len < max_u16_t", rem_len < 0xffff);
+ q->tot_len = (u16_t)rem_len;
+ /* this pbuf length is pool size, unless smaller sized tail */
+ q->len = LWIP_MIN((u16_t)rem_len, PBUF_POOL_BUFSIZE_ALIGNED);
+ q->payload = (void *)((u8_t *)q + SIZEOF_STRUCT_PBUF);
+ LWIP_ASSERT("pbuf_alloc: pbuf q->payload properly aligned",
+ ((mem_ptr_t)q->payload % MEM_ALIGNMENT) == 0);
+ LWIP_ASSERT("check p->payload + p->len does not overflow pbuf",
+ ((u8_t*)p->payload + p->len <=
+ (u8_t*)p + SIZEOF_STRUCT_PBUF + PBUF_POOL_BUFSIZE_ALIGNED));
+ q->ref = 1;
+ /* calculate remaining length to be allocated */
+ rem_len -= q->len;
+ /* remember this pbuf for linkage in next iteration */
+ r = q;
+ }
+ /* end of chain */
+ /*r->next = NULL;*/
+
+ break;
+ case PBUF_RAM:
+ {
+ mem_size_t alloc_len = LWIP_MEM_ALIGN_SIZE(SIZEOF_STRUCT_PBUF + offset) + LWIP_MEM_ALIGN_SIZE(length);
+
+ /* bug #50040: Check for integer overflow when calculating alloc_len */
+ if (alloc_len < LWIP_MEM_ALIGN_SIZE(length)) {
+ return NULL;
+ }
+
+ /* If pbuf is to be allocated in RAM, allocate memory for it. */
+ p = (struct pbuf*)mem_malloc(alloc_len);
+ }
+
+ if (p == NULL) {
+ return NULL;
+ }
+ /* Set up internal structure of the pbuf. */
+ p->payload = LWIP_MEM_ALIGN((void *)((u8_t *)p + SIZEOF_STRUCT_PBUF + offset));
+ p->len = p->tot_len = length;
+ p->next = NULL;
+ p->type = type;
+
+ LWIP_ASSERT("pbuf_alloc: pbuf->payload properly aligned",
+ ((mem_ptr_t)p->payload % MEM_ALIGNMENT) == 0);
+ break;
+ /* pbuf references existing (non-volatile static constant) ROM payload? */
+ case PBUF_ROM:
+ /* pbuf references existing (externally allocated) RAM payload? */
+ case PBUF_REF:
+ /* only allocate memory for the pbuf structure */
+ p = (struct pbuf *)memp_malloc(MEMP_PBUF);
+ if (p == NULL) {
+ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+ ("pbuf_alloc: Could not allocate MEMP_PBUF for PBUF_%s.\n",
+ (type == PBUF_ROM) ? "ROM" : "REF"));
+ return NULL;
+ }
+ /* caller must set this field properly, afterwards */
+ p->payload = NULL;
+ p->len = p->tot_len = length;
+ p->next = NULL;
+ p->type = type;
+ break;
+ default:
+ LWIP_ASSERT("pbuf_alloc: erroneous type", 0);
+ return NULL;
+ }
+ /* set reference count */
+ p->ref = 1;
+ /* set flags */
+ p->flags = 0;
+ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc(length=%"U16_F") == %p\n", length, (void *)p));
+ return p;
+}
+
+#if LWIP_SUPPORT_CUSTOM_PBUF
+/**
+ * @ingroup pbuf
+ * Initialize a custom pbuf (already allocated).
+ *
+ * @param l flag to define header size
+ * @param length size of the pbuf's payload
+ * @param type type of the pbuf (only used to treat the pbuf accordingly, as
+ * this function allocates no memory)
+ * @param p pointer to the custom pbuf to initialize (already allocated)
+ * @param payload_mem pointer to the buffer that is used for payload and headers,
+ * must be at least big enough to hold 'length' plus the header size,
+ * may be NULL if set later.
+ * ATTENTION: The caller is responsible for correct alignment of this buffer!!
+ * @param payload_mem_len the size of the 'payload_mem' buffer, must be at least
+ * big enough to hold 'length' plus the header size
+ */
+struct pbuf*
+pbuf_alloced_custom(pbuf_layer l, u16_t length, pbuf_type type, struct pbuf_custom *p,
+ void *payload_mem, u16_t payload_mem_len)
+{
+ u16_t offset;
+ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloced_custom(length=%"U16_F")\n", length));
+
+ /* determine header offset */
+ switch (l) {
+ case PBUF_TRANSPORT:
+ /* add room for transport (often TCP) layer header */
+ offset = PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN;
+ break;
+ case PBUF_IP:
+ /* add room for IP layer header */
+ offset = PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN + PBUF_IP_HLEN;
+ break;
+ case PBUF_LINK:
+ /* add room for link layer header */
+ offset = PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN;
+ break;
+ case PBUF_RAW_TX:
+ /* add room for encapsulating link layer headers (e.g. 802.11) */
+ offset = PBUF_LINK_ENCAPSULATION_HLEN;
+ break;
+ case PBUF_RAW:
+ offset = 0;
+ break;
+ default:
+ LWIP_ASSERT("pbuf_alloced_custom: bad pbuf layer", 0);
+ return NULL;
+ }
+
+ if (LWIP_MEM_ALIGN_SIZE(offset) + length > payload_mem_len) {
+ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_LEVEL_WARNING, ("pbuf_alloced_custom(length=%"U16_F") buffer too short\n", length));
+ return NULL;
+ }
+
+ p->pbuf.next = NULL;
+ if (payload_mem != NULL) {
+ p->pbuf.payload = (u8_t *)payload_mem + LWIP_MEM_ALIGN_SIZE(offset);
+ } else {
+ p->pbuf.payload = NULL;
+ }
+ p->pbuf.flags = PBUF_FLAG_IS_CUSTOM;
+ p->pbuf.len = p->pbuf.tot_len = length;
+ p->pbuf.type = type;
+ p->pbuf.ref = 1;
+ return &p->pbuf;
+}
+#endif /* LWIP_SUPPORT_CUSTOM_PBUF */
+
+/**
+ * @ingroup pbuf
+ * Shrink a pbuf chain to a desired length.
+ *
+ * @param p pbuf to shrink.
+ * @param new_len desired new length of pbuf chain
+ *
+ * Depending on the desired length, the first few pbufs in a chain might
+ * be skipped and left unchanged. The new last pbuf in the chain will be
+ * resized, and any remaining pbufs will be freed.
+ *
+ * @note If the pbuf is ROM/REF, only the ->tot_len and ->len fields are adjusted.
+ * @note May not be called on a packet queue.
+ *
+ * @note Despite its name, pbuf_realloc cannot grow the size of a pbuf (chain).
+ */
+void
+pbuf_realloc(struct pbuf *p, u16_t new_len)
+{
+ struct pbuf *q;
+ u16_t rem_len; /* remaining length */
+ s32_t grow;
+
+ LWIP_ASSERT("pbuf_realloc: p != NULL", p != NULL);
+ LWIP_ASSERT("pbuf_realloc: sane p->type", p->type == PBUF_POOL ||
+ p->type == PBUF_ROM ||
+ p->type == PBUF_RAM ||
+ p->type == PBUF_REF);
+
+ /* desired length larger than current length? */
+ if (new_len >= p->tot_len) {
+ /* enlarging not yet supported */
+ return;
+ }
+
+ /* the pbuf chain grows by (new_len - p->tot_len) bytes
+ * (which may be negative in case of shrinking) */
+ grow = new_len - p->tot_len;
+
+ /* first, step over any pbufs that should remain in the chain */
+ rem_len = new_len;
+ q = p;
+ /* should this pbuf be kept? */
+ while (rem_len > q->len) {
+ /* decrease remaining length by pbuf length */
+ rem_len -= q->len;
+ /* decrease total length indicator */
+ LWIP_ASSERT("grow < max_u16_t", grow < 0xffff);
+ q->tot_len += (u16_t)grow;
+ /* proceed to next pbuf in chain */
+ q = q->next;
+ LWIP_ASSERT("pbuf_realloc: q != NULL", q != NULL);
+ }
+ /* we have now reached the new last pbuf (in q) */
+ /* rem_len == desired length for pbuf q */
+
+ /* shrink allocated memory for PBUF_RAM */
+ /* (other types merely adjust their length fields */
+ if ((q->type == PBUF_RAM) && (rem_len != q->len)
+#if LWIP_SUPPORT_CUSTOM_PBUF
+ && ((q->flags & PBUF_FLAG_IS_CUSTOM) == 0)
+#endif /* LWIP_SUPPORT_CUSTOM_PBUF */
+ ) {
+ /* reallocate and adjust the length of the pbuf that will be split */
+ q = (struct pbuf *)mem_trim(q, (u16_t)((u8_t *)q->payload - (u8_t *)q) + rem_len);
+ LWIP_ASSERT("mem_trim returned q == NULL", q != NULL);
+ }
+ /* adjust length fields for new last pbuf */
+ q->len = rem_len;
+ q->tot_len = q->len;
+
+ /* any remaining pbufs in chain? */
+ if (q->next != NULL) {
+ /* free remaining pbufs in chain */
+ pbuf_free(q->next);
+ }
+ /* q is last packet in chain */
+ q->next = NULL;
+
+}
+
+/**
+ * Adjusts the payload pointer to hide or reveal headers in the payload.
+ * @see pbuf_header.
+ *
+ * @param p pbuf to change the header size.
+ * @param header_size_increment Number of bytes to increment header size.
+ * @param force Allow 'header_size_increment > 0' for PBUF_REF/PBUF_ROM types
+ *
+ * @return non-zero on failure, zero on success.
+ *
+ */
+static u8_t
+pbuf_header_impl(struct pbuf *p, s16_t header_size_increment, u8_t force)
+{
+ u16_t type;
+ void *payload;
+ u16_t increment_magnitude;
+
+ LWIP_ASSERT("p != NULL", p != NULL);
+ if ((header_size_increment == 0) || (p == NULL)) {
+ return 0;
+ }
+
+ if (header_size_increment < 0) {
+ increment_magnitude = (u16_t)-header_size_increment;
+ /* Check that we aren't going to move off the end of the pbuf */
+ LWIP_ERROR("increment_magnitude <= p->len", (increment_magnitude <= p->len), return 1;);
+ } else {
+ increment_magnitude = (u16_t)header_size_increment;
+#if 0
+ /* Can't assert these as some callers speculatively call
+ pbuf_header() to see if it's OK. Will return 1 below instead. */
+ /* Check that we've got the correct type of pbuf to work with */
+ LWIP_ASSERT("p->type == PBUF_RAM || p->type == PBUF_POOL",
+ p->type == PBUF_RAM || p->type == PBUF_POOL);
+ /* Check that we aren't going to move off the beginning of the pbuf */
+ LWIP_ASSERT("p->payload - increment_magnitude >= p + SIZEOF_STRUCT_PBUF",
+ (u8_t *)p->payload - increment_magnitude >= (u8_t *)p + SIZEOF_STRUCT_PBUF);
+#endif
+ }
+
+ type = p->type;
+ /* remember current payload pointer */
+ payload = p->payload;
+
+ /* pbuf types containing payloads? */
+ if (type == PBUF_RAM || type == PBUF_POOL) {
+ /* set new payload pointer */
+ p->payload = (u8_t *)p->payload - header_size_increment;
+ /* boundary check fails? */
+ if ((u8_t *)p->payload < (u8_t *)p + SIZEOF_STRUCT_PBUF) {
+ LWIP_DEBUGF( PBUF_DEBUG | LWIP_DBG_TRACE,
+ ("pbuf_header: failed as %p < %p (not enough space for new header size)\n",
+ (void *)p->payload, (void *)((u8_t *)p + SIZEOF_STRUCT_PBUF)));
+ /* restore old payload pointer */
+ p->payload = payload;
+ /* bail out unsuccessfully */
+ return 1;
+ }
+ /* pbuf types referring to external payloads? */
+ } else if (type == PBUF_REF || type == PBUF_ROM) {
+ /* hide a header in the payload? */
+ if ((header_size_increment < 0) && (increment_magnitude <= p->len)) {
+ /* increase payload pointer */
+ p->payload = (u8_t *)p->payload - header_size_increment;
+ } else if ((header_size_increment > 0) && force) {
+ p->payload = (u8_t *)p->payload - header_size_increment;
+ } else {
+ /* cannot expand payload to front (yet!)
+ * bail out unsuccessfully */
+ return 1;
+ }
+ } else {
+ /* Unknown type */
+ LWIP_ASSERT("bad pbuf type", 0);
+ return 1;
+ }
+ /* modify pbuf length fields */
+ p->len += header_size_increment;
+ p->tot_len += header_size_increment;
+
+ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_header: old %p new %p (%"S16_F")\n",
+ (void *)payload, (void *)p->payload, header_size_increment));
+
+ return 0;
+}
+
+/**
+ * Adjusts the payload pointer to hide or reveal headers in the payload.
+ *
+ * Adjusts the ->payload pointer so that space for a header
+ * (dis)appears in the pbuf payload.
+ *
+ * The ->payload, ->tot_len and ->len fields are adjusted.
+ *
+ * @param p pbuf to change the header size.
+ * @param header_size_increment Number of bytes to increment header size which
+ * increases the size of the pbuf. New space is on the front.
+ * (Using a negative value decreases the header size.)
+ * If hdr_size_inc is 0, this function does nothing and returns successful.
+ *
+ * PBUF_ROM and PBUF_REF type buffers cannot have their sizes increased, so
+ * the call will fail. A check is made that the increase in header size does
+ * not move the payload pointer in front of the start of the buffer.
+ * @return non-zero on failure, zero on success.
+ *
+ */
+u8_t
+pbuf_header(struct pbuf *p, s16_t header_size_increment)
+{
+ return pbuf_header_impl(p, header_size_increment, 0);
+}
+
+/**
+ * Same as pbuf_header but does not check if 'header_size > 0' is allowed.
+ * This is used internally only, to allow PBUF_REF for RX.
+ */
+u8_t
+pbuf_header_force(struct pbuf *p, s16_t header_size_increment)
+{
+ return pbuf_header_impl(p, header_size_increment, 1);
+}
+
+/**
+ * @ingroup pbuf
+ * Dereference a pbuf chain or queue and deallocate any no-longer-used
+ * pbufs at the head of this chain or queue.
+ *
+ * Decrements the pbuf reference count. If it reaches zero, the pbuf is
+ * deallocated.
+ *
+ * For a pbuf chain, this is repeated for each pbuf in the chain,
+ * up to the first pbuf which has a non-zero reference count after
+ * decrementing. So, when all reference counts are one, the whole
+ * chain is free'd.
+ *
+ * @param p The pbuf (chain) to be dereferenced.
+ *
+ * @return the number of pbufs that were de-allocated
+ * from the head of the chain.
+ *
+ * @note MUST NOT be called on a packet queue (Not verified to work yet).
+ * @note the reference counter of a pbuf equals the number of pointers
+ * that refer to the pbuf (or into the pbuf).
+ *
+ * @internal examples:
+ *
+ * Assuming existing chains a->b->c with the following reference
+ * counts, calling pbuf_free(a) results in:
+ *
+ * 1->2->3 becomes ...1->3
+ * 3->3->3 becomes 2->3->3
+ * 1->1->2 becomes ......1
+ * 2->1->1 becomes 1->1->1
+ * 1->1->1 becomes .......
+ *
+ */
+u8_t
+pbuf_free(struct pbuf *p)
+{
+ u16_t type;
+ struct pbuf *q;
+ u8_t count;
+
+ if (p == NULL) {
+ LWIP_ASSERT("p != NULL", p != NULL);
+ /* if assertions are disabled, proceed with debug output */
+ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+ ("pbuf_free(p == NULL) was called.\n"));
+ return 0;
+ }
+ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_free(%p)\n", (void *)p));
+
+ PERF_START;
+
+ LWIP_ASSERT("pbuf_free: sane type",
+ p->type == PBUF_RAM || p->type == PBUF_ROM ||
+ p->type == PBUF_REF || p->type == PBUF_POOL);
+
+ count = 0;
+ /* de-allocate all consecutive pbufs from the head of the chain that
+ * obtain a zero reference count after decrementing*/
+ while (p != NULL) {
+ u16_t ref;
+ SYS_ARCH_DECL_PROTECT(old_level);
+ /* Since decrementing ref cannot be guaranteed to be a single machine operation
+ * we must protect it. We put the new ref into a local variable to prevent
+ * further protection. */
+ SYS_ARCH_PROTECT(old_level);
+ /* all pbufs in a chain are referenced at least once */
+ LWIP_ASSERT("pbuf_free: p->ref > 0", p->ref > 0);
+ /* decrease reference count (number of pointers to pbuf) */
+ ref = --(p->ref);
+ SYS_ARCH_UNPROTECT(old_level);
+ /* this pbuf is no longer referenced to? */
+ if (ref == 0) {
+ /* remember next pbuf in chain for next iteration */
+ q = p->next;
+ LWIP_DEBUGF( PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_free: deallocating %p\n", (void *)p));
+ type = p->type;
+#if LWIP_SUPPORT_CUSTOM_PBUF
+ /* is this a custom pbuf? */
+ if ((p->flags & PBUF_FLAG_IS_CUSTOM) != 0) {
+ struct pbuf_custom *pc = (struct pbuf_custom*)p;
+ LWIP_ASSERT("pc->custom_free_function != NULL", pc->custom_free_function != NULL);
+ pc->custom_free_function(p);
+ } else
+#endif /* LWIP_SUPPORT_CUSTOM_PBUF */
+ {
+ /* is this a pbuf from the pool? */
+ if (type == PBUF_POOL) {
+ memp_free(MEMP_PBUF_POOL, p);
+ /* is this a ROM or RAM referencing pbuf? */
+ } else if (type == PBUF_ROM || type == PBUF_REF) {
+ memp_free(MEMP_PBUF, p);
+ /* type == PBUF_RAM */
+ } else {
+ mem_free(p);
+ }
+ }
+ count++;
+ /* proceed to next pbuf */
+ p = q;
+ /* p->ref > 0, this pbuf is still referenced to */
+ /* (and so the remaining pbufs in chain as well) */
+ } else {
+ LWIP_DEBUGF( PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_free: %p has ref %"U16_F", ending here.\n", (void *)p, ref));
+ /* stop walking through the chain */
+ p = NULL;
+ }
+ }
+ PERF_STOP("pbuf_free");
+ /* return number of de-allocated pbufs */
+ return count;
+}
+
+/**
+ * Count number of pbufs in a chain
+ *
+ * @param p first pbuf of chain
+ * @return the number of pbufs in a chain
+ */
+u16_t
+pbuf_clen(const struct pbuf *p)
+{
+ u16_t len;
+
+ len = 0;
+ while (p != NULL) {
+ ++len;
+ p = p->next;
+ }
+ return len;
+}
+
+/**
+ * @ingroup pbuf
+ * Increment the reference count of the pbuf.
+ *
+ * @param p pbuf to increase reference counter of
+ *
+ */
+void
+pbuf_ref(struct pbuf *p)
+{
+ /* pbuf given? */
+ if (p != NULL) {
+ SYS_ARCH_INC(p->ref, 1);
+ LWIP_ASSERT("pbuf ref overflow", p->ref > 0);
+ }
+}
+
+/**
+ * @ingroup pbuf
+ * Concatenate two pbufs (each may be a pbuf chain) and take over
+ * the caller's reference of the tail pbuf.
+ *
+ * @note The caller MAY NOT reference the tail pbuf afterwards.
+ * Use pbuf_chain() for that purpose.
+ *
+ * @see pbuf_chain()
+ */
+void
+pbuf_cat(struct pbuf *h, struct pbuf *t)
+{
+ struct pbuf *p;
+
+ LWIP_ERROR("(h != NULL) && (t != NULL) (programmer violates API)",
+ ((h != NULL) && (t != NULL)), return;);
+
+ /* proceed to last pbuf of chain */
+ for (p = h; p->next != NULL; p = p->next) {
+ /* add total length of second chain to all totals of first chain */
+ p->tot_len += t->tot_len;
+ }
+ /* { p is last pbuf of first h chain, p->next == NULL } */
+ LWIP_ASSERT("p->tot_len == p->len (of last pbuf in chain)", p->tot_len == p->len);
+ LWIP_ASSERT("p->next == NULL", p->next == NULL);
+ /* add total length of second chain to last pbuf total of first chain */
+ p->tot_len += t->tot_len;
+ /* chain last pbuf of head (p) with first of tail (t) */
+ p->next = t;
+ /* p->next now references t, but the caller will drop its reference to t,
+ * so netto there is no change to the reference count of t.
+ */
+}
+
+/**
+ * @ingroup pbuf
+ * Chain two pbufs (or pbuf chains) together.
+ *
+ * The caller MUST call pbuf_free(t) once it has stopped
+ * using it. Use pbuf_cat() instead if you no longer use t.
+ *
+ * @param h head pbuf (chain)
+ * @param t tail pbuf (chain)
+ * @note The pbufs MUST belong to the same packet.
+ * @note MAY NOT be called on a packet queue.
+ *
+ * The ->tot_len fields of all pbufs of the head chain are adjusted.
+ * The ->next field of the last pbuf of the head chain is adjusted.
+ * The ->ref field of the first pbuf of the tail chain is adjusted.
+ *
+ */
+void
+pbuf_chain(struct pbuf *h, struct pbuf *t)
+{
+ pbuf_cat(h, t);
+ /* t is now referenced by h */
+ pbuf_ref(t);
+ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_chain: %p references %p\n", (void *)h, (void *)t));
+}
+
+/**
+ * Dechains the first pbuf from its succeeding pbufs in the chain.
+ *
+ * Makes p->tot_len field equal to p->len.
+ * @param p pbuf to dechain
+ * @return remainder of the pbuf chain, or NULL if it was de-allocated.
+ * @note May not be called on a packet queue.
+ */
+struct pbuf *
+pbuf_dechain(struct pbuf *p)
+{
+ struct pbuf *q;
+ u8_t tail_gone = 1;
+ /* tail */
+ q = p->next;
+ /* pbuf has successor in chain? */
+ if (q != NULL) {
+ /* assert tot_len invariant: (p->tot_len == p->len + (p->next? p->next->tot_len: 0) */
+ LWIP_ASSERT("p->tot_len == p->len + q->tot_len", q->tot_len == p->tot_len - p->len);
+ /* enforce invariant if assertion is disabled */
+ q->tot_len = p->tot_len - p->len;
+ /* decouple pbuf from remainder */
+ p->next = NULL;
+ /* total length of pbuf p is its own length only */
+ p->tot_len = p->len;
+ /* q is no longer referenced by p, free it */
+ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_dechain: unreferencing %p\n", (void *)q));
+ tail_gone = pbuf_free(q);
+ if (tail_gone > 0) {
+ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE,
+ ("pbuf_dechain: deallocated %p (as it is no longer referenced)\n", (void *)q));
+ }
+ /* return remaining tail or NULL if deallocated */
+ }
+ /* assert tot_len invariant: (p->tot_len == p->len + (p->next? p->next->tot_len: 0) */
+ LWIP_ASSERT("p->tot_len == p->len", p->tot_len == p->len);
+ return ((tail_gone > 0) ? NULL : q);
+}
+
+/**
+ * @ingroup pbuf
+ * Create PBUF_RAM copies of pbufs.
+ *
+ * Used to queue packets on behalf of the lwIP stack, such as
+ * ARP based queueing.
+ *
+ * @note You MUST explicitly use p = pbuf_take(p);
+ *
+ * @note Only one packet is copied, no packet queue!
+ *
+ * @param p_to pbuf destination of the copy
+ * @param p_from pbuf source of the copy
+ *
+ * @return ERR_OK if pbuf was copied
+ * ERR_ARG if one of the pbufs is NULL or p_to is not big
+ * enough to hold p_from
+ */
+err_t
+pbuf_copy(struct pbuf *p_to, const struct pbuf *p_from)
+{
+ u16_t offset_to=0, offset_from=0, len;
+
+ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_copy(%p, %p)\n",
+ (const void*)p_to, (const void*)p_from));
+
+ /* is the target big enough to hold the source? */
+ LWIP_ERROR("pbuf_copy: target not big enough to hold source", ((p_to != NULL) &&
+ (p_from != NULL) && (p_to->tot_len >= p_from->tot_len)), return ERR_ARG;);
+
+ /* iterate through pbuf chain */
+ do
+ {
+ /* copy one part of the original chain */
+ if ((p_to->len - offset_to) >= (p_from->len - offset_from)) {
+ /* complete current p_from fits into current p_to */
+ len = p_from->len - offset_from;
+ } else {
+ /* current p_from does not fit into current p_to */
+ len = p_to->len - offset_to;
+ }
+ MEMCPY((u8_t*)p_to->payload + offset_to, (u8_t*)p_from->payload + offset_from, len);
+ offset_to += len;
+ offset_from += len;
+ LWIP_ASSERT("offset_to <= p_to->len", offset_to <= p_to->len);
+ LWIP_ASSERT("offset_from <= p_from->len", offset_from <= p_from->len);
+ if (offset_from >= p_from->len) {
+ /* on to next p_from (if any) */
+ offset_from = 0;
+ p_from = p_from->next;
+ }
+ if (offset_to == p_to->len) {
+ /* on to next p_to (if any) */
+ offset_to = 0;
+ p_to = p_to->next;
+ LWIP_ERROR("p_to != NULL", (p_to != NULL) || (p_from == NULL) , return ERR_ARG;);
+ }
+
+ if ((p_from != NULL) && (p_from->len == p_from->tot_len)) {
+ /* don't copy more than one packet! */
+ LWIP_ERROR("pbuf_copy() does not allow packet queues!",
+ (p_from->next == NULL), return ERR_VAL;);
+ }
+ if ((p_to != NULL) && (p_to->len == p_to->tot_len)) {
+ /* don't copy more than one packet! */
+ LWIP_ERROR("pbuf_copy() does not allow packet queues!",
+ (p_to->next == NULL), return ERR_VAL;);
+ }
+ } while (p_from);
+ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_copy: end of chain reached.\n"));
+ return ERR_OK;
+}
+
+/**
+ * @ingroup pbuf
+ * Copy (part of) the contents of a packet buffer
+ * to an application supplied buffer.
+ *
+ * @param buf the pbuf from which to copy data
+ * @param dataptr the application supplied buffer
+ * @param len length of data to copy (dataptr must be big enough). No more
+ * than buf->tot_len will be copied, irrespective of len
+ * @param offset offset into the packet buffer from where to begin copying len bytes
+ * @return the number of bytes copied, or 0 on failure
+ */
+u16_t
+pbuf_copy_partial(const struct pbuf *buf, void *dataptr, u16_t len, u16_t offset)
+{
+ const struct pbuf *p;
+ u16_t left;
+ u16_t buf_copy_len;
+ u16_t copied_total = 0;
+
+ LWIP_ERROR("pbuf_copy_partial: invalid buf", (buf != NULL), return 0;);
+ LWIP_ERROR("pbuf_copy_partial: invalid dataptr", (dataptr != NULL), return 0;);
+
+ left = 0;
+
+ if ((buf == NULL) || (dataptr == NULL)) {
+ return 0;
+ }
+
+ /* Note some systems use byte copy if dataptr or one of the pbuf payload pointers are unaligned. */
+ for (p = buf; len != 0 && p != NULL; p = p->next) {
+ if ((offset != 0) && (offset >= p->len)) {
+ /* don't copy from this buffer -> on to the next */
+ offset -= p->len;
+ } else {
+ /* copy from this buffer. maybe only partially. */
+ buf_copy_len = p->len - offset;
+ if (buf_copy_len > len) {
+ buf_copy_len = len;
+ }
+ /* copy the necessary parts of the buffer */
+ MEMCPY(&((char*)dataptr)[left], &((char*)p->payload)[offset], buf_copy_len);
+ copied_total += buf_copy_len;
+ left += buf_copy_len;
+ len -= buf_copy_len;
+ offset = 0;
+ }
+ }
+ return copied_total;
+}
+
+#if LWIP_TCP && TCP_QUEUE_OOSEQ && LWIP_WND_SCALE
+/**
+ * This method modifies a 'pbuf chain', so that its total length is
+ * smaller than 64K. The remainder of the original pbuf chain is stored
+ * in *rest.
+ * This function never creates new pbufs, but splits an existing chain
+ * in two parts. The tot_len of the modified packet queue will likely be
+ * smaller than 64K.
+ * 'packet queues' are not supported by this function.
+ *
+ * @param p the pbuf queue to be split
+ * @param rest pointer to store the remainder (after the first 64K)
+ */
+void pbuf_split_64k(struct pbuf *p, struct pbuf **rest)
+{
+ *rest = NULL;
+ if ((p != NULL) && (p->next != NULL)) {
+ u16_t tot_len_front = p->len;
+ struct pbuf *i = p;
+ struct pbuf *r = p->next;
+
+ /* continue until the total length (summed up as u16_t) overflows */
+ while ((r != NULL) && ((u16_t)(tot_len_front + r->len) > tot_len_front)) {
+ tot_len_front += r->len;
+ i = r;
+ r = r->next;
+ }
+ /* i now points to last packet of the first segment. Set next
+ pointer to NULL */
+ i->next = NULL;
+
+ if (r != NULL) {
+ /* Update the tot_len field in the first part */
+ for (i = p; i != NULL; i = i->next) {
+ i->tot_len -= r->tot_len;
+ LWIP_ASSERT("tot_len/len mismatch in last pbuf",
+ (i->next != NULL) || (i->tot_len == i->len));
+ }
+ if (p->flags & PBUF_FLAG_TCP_FIN) {
+ r->flags |= PBUF_FLAG_TCP_FIN;
+ }
+
+ /* tot_len field in rest does not need modifications */
+ /* reference counters do not need modifications */
+ *rest = r;
+ }
+ }
+}
+#endif /* LWIP_TCP && TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */
+
+/* Actual implementation of pbuf_skip() but returning const pointer... */
+static const struct pbuf*
+pbuf_skip_const(const struct pbuf* in, u16_t in_offset, u16_t* out_offset)
+{
+ u16_t offset_left = in_offset;
+ const struct pbuf* q = in;
+
+ /* get the correct pbuf */
+ while ((q != NULL) && (q->len <= offset_left)) {
+ offset_left -= q->len;
+ q = q->next;
+ }
+ if (out_offset != NULL) {
+ *out_offset = offset_left;
+ }
+ return q;
+}
+
+/**
+ * @ingroup pbuf
+ * Skip a number of bytes at the start of a pbuf
+ *
+ * @param in input pbuf
+ * @param in_offset offset to skip
+ * @param out_offset resulting offset in the returned pbuf
+ * @return the pbuf in the queue where the offset is
+ */
+struct pbuf*
+pbuf_skip(struct pbuf* in, u16_t in_offset, u16_t* out_offset)
+{
+ const struct pbuf* out = pbuf_skip_const(in, in_offset, out_offset);
+ return LWIP_CONST_CAST(struct pbuf*, out);
+}
+
+/**
+ * @ingroup pbuf
+ * Copy application supplied data into a pbuf.
+ * This function can only be used to copy the equivalent of buf->tot_len data.
+ *
+ * @param buf pbuf to fill with data
+ * @param dataptr application supplied data buffer
+ * @param len length of the application supplied data buffer
+ *
+ * @return ERR_OK if successful, ERR_MEM if the pbuf is not big enough
+ */
+err_t
+pbuf_take(struct pbuf *buf, const void *dataptr, u16_t len)
+{
+ struct pbuf *p;
+ u16_t buf_copy_len;
+ u16_t total_copy_len = len;
+ u16_t copied_total = 0;
+
+ LWIP_ERROR("pbuf_take: invalid buf", (buf != NULL), return ERR_ARG;);
+ LWIP_ERROR("pbuf_take: invalid dataptr", (dataptr != NULL), return ERR_ARG;);
+ LWIP_ERROR("pbuf_take: buf not large enough", (buf->tot_len >= len), return ERR_MEM;);
+
+ if ((buf == NULL) || (dataptr == NULL) || (buf->tot_len < len)) {
+ return ERR_ARG;
+ }
+
+ /* Note some systems use byte copy if dataptr or one of the pbuf payload pointers are unaligned. */
+ for (p = buf; total_copy_len != 0; p = p->next) {
+ LWIP_ASSERT("pbuf_take: invalid pbuf", p != NULL);
+ buf_copy_len = total_copy_len;
+ if (buf_copy_len > p->len) {
+ /* this pbuf cannot hold all remaining data */
+ buf_copy_len = p->len;
+ }
+ /* copy the necessary parts of the buffer */
+ MEMCPY(p->payload, &((const char*)dataptr)[copied_total], buf_copy_len);
+ total_copy_len -= buf_copy_len;
+ copied_total += buf_copy_len;
+ }
+ LWIP_ASSERT("did not copy all data", total_copy_len == 0 && copied_total == len);
+ return ERR_OK;
+}
+
+/**
+ * @ingroup pbuf
+ * Same as pbuf_take() but puts data at an offset
+ *
+ * @param buf pbuf to fill with data
+ * @param dataptr application supplied data buffer
+ * @param len length of the application supplied data buffer
+ * @param offset offset in pbuf where to copy dataptr to
+ *
+ * @return ERR_OK if successful, ERR_MEM if the pbuf is not big enough
+ */
+err_t
+pbuf_take_at(struct pbuf *buf, const void *dataptr, u16_t len, u16_t offset)
+{
+ u16_t target_offset;
+ struct pbuf* q = pbuf_skip(buf, offset, &target_offset);
+
+ /* return requested data if pbuf is OK */
+ if ((q != NULL) && (q->tot_len >= target_offset + len)) {
+ u16_t remaining_len = len;
+ const u8_t* src_ptr = (const u8_t*)dataptr;
+ /* copy the part that goes into the first pbuf */
+ u16_t first_copy_len = LWIP_MIN(q->len - target_offset, len);
+ MEMCPY(((u8_t*)q->payload) + target_offset, dataptr, first_copy_len);
+ remaining_len -= first_copy_len;
+ src_ptr += first_copy_len;
+ if (remaining_len > 0) {
+ return pbuf_take(q->next, src_ptr, remaining_len);
+ }
+ return ERR_OK;
+ }
+ return ERR_MEM;
+}
+
+/**
+ * @ingroup pbuf
+ * Creates a single pbuf out of a queue of pbufs.
+ *
+ * @remark: Either the source pbuf 'p' is freed by this function or the original
+ * pbuf 'p' is returned, therefore the caller has to check the result!
+ *
+ * @param p the source pbuf
+ * @param layer pbuf_layer of the new pbuf
+ *
+ * @return a new, single pbuf (p->next is NULL)
+ * or the old pbuf if allocation fails
+ */
+struct pbuf*
+pbuf_coalesce(struct pbuf *p, pbuf_layer layer)
+{
+ struct pbuf *q;
+ err_t err;
+ if (p->next == NULL) {
+ return p;
+ }
+ q = pbuf_alloc(layer, p->tot_len, PBUF_RAM);
+ if (q == NULL) {
+ /* @todo: what do we do now? */
+ return p;
+ }
+ err = pbuf_copy(q, p);
+ LWIP_UNUSED_ARG(err); /* in case of LWIP_NOASSERT */
+ LWIP_ASSERT("pbuf_copy failed", err == ERR_OK);
+ pbuf_free(p);
+ return q;
+}
+
+#if LWIP_CHECKSUM_ON_COPY
+/**
+ * Copies data into a single pbuf (*not* into a pbuf queue!) and updates
+ * the checksum while copying
+ *
+ * @param p the pbuf to copy data into
+ * @param start_offset offset of p->payload where to copy the data to
+ * @param dataptr data to copy into the pbuf
+ * @param len length of data to copy into the pbuf
+ * @param chksum pointer to the checksum which is updated
+ * @return ERR_OK if successful, another error if the data does not fit
+ * within the (first) pbuf (no pbuf queues!)
+ */
+err_t
+pbuf_fill_chksum(struct pbuf *p, u16_t start_offset, const void *dataptr,
+ u16_t len, u16_t *chksum)
+{
+ u32_t acc;
+ u16_t copy_chksum;
+ char *dst_ptr;
+ LWIP_ASSERT("p != NULL", p != NULL);
+ LWIP_ASSERT("dataptr != NULL", dataptr != NULL);
+ LWIP_ASSERT("chksum != NULL", chksum != NULL);
+ LWIP_ASSERT("len != 0", len != 0);
+
+ if ((start_offset >= p->len) || (start_offset + len > p->len)) {
+ return ERR_ARG;
+ }
+
+ dst_ptr = ((char*)p->payload) + start_offset;
+ copy_chksum = LWIP_CHKSUM_COPY(dst_ptr, dataptr, len);
+ if ((start_offset & 1) != 0) {
+ copy_chksum = SWAP_BYTES_IN_WORD(copy_chksum);
+ }
+ acc = *chksum;
+ acc += copy_chksum;
+ *chksum = FOLD_U32T(acc);
+ return ERR_OK;
+}
+#endif /* LWIP_CHECKSUM_ON_COPY */
+
+/**
+ * @ingroup pbuf
+ * Get one byte from the specified position in a pbuf
+ * WARNING: returns zero for offset >= p->tot_len
+ *
+ * @param p pbuf to parse
+ * @param offset offset into p of the byte to return
+ * @return byte at an offset into p OR ZERO IF 'offset' >= p->tot_len
+ */
+u8_t
+pbuf_get_at(const struct pbuf* p, u16_t offset)
+{
+ int ret = pbuf_try_get_at(p, offset);
+ if (ret >= 0) {
+ return (u8_t)ret;
+ }
+ return 0;
+}
+
+/**
+ * @ingroup pbuf
+ * Get one byte from the specified position in a pbuf
+ *
+ * @param p pbuf to parse
+ * @param offset offset into p of the byte to return
+ * @return byte at an offset into p [0..0xFF] OR negative if 'offset' >= p->tot_len
+ */
+int
+pbuf_try_get_at(const struct pbuf* p, u16_t offset)
+{
+ u16_t q_idx;
+ const struct pbuf* q = pbuf_skip_const(p, offset, &q_idx);
+
+ /* return requested data if pbuf is OK */
+ if ((q != NULL) && (q->len > q_idx)) {
+ return ((u8_t*)q->payload)[q_idx];
+ }
+ return -1;
+}
+
+/**
+ * @ingroup pbuf
+ * Put one byte to the specified position in a pbuf
+ * WARNING: silently ignores offset >= p->tot_len
+ *
+ * @param p pbuf to fill
+ * @param offset offset into p of the byte to write
+ * @param data byte to write at an offset into p
+ */
+void
+pbuf_put_at(struct pbuf* p, u16_t offset, u8_t data)
+{
+ u16_t q_idx;
+ struct pbuf* q = pbuf_skip(p, offset, &q_idx);
+
+ /* write requested data if pbuf is OK */
+ if ((q != NULL) && (q->len > q_idx)) {
+ ((u8_t*)q->payload)[q_idx] = data;
+ }
+}
+
+/**
+ * @ingroup pbuf
+ * Compare pbuf contents at specified offset with memory s2, both of length n
+ *
+ * @param p pbuf to compare
+ * @param offset offset into p at which to start comparing
+ * @param s2 buffer to compare
+ * @param n length of buffer to compare
+ * @return zero if equal, nonzero otherwise
+ * (0xffff if p is too short, diffoffset+1 otherwise)
+ */
+u16_t
+pbuf_memcmp(const struct pbuf* p, u16_t offset, const void* s2, u16_t n)
+{
+ u16_t start = offset;
+ const struct pbuf* q = p;
+ u16_t i;
+
+ /* pbuf long enough to perform check? */
+ if(p->tot_len < (offset + n)) {
+ return 0xffff;
+ }
+
+ /* get the correct pbuf from chain. We know it succeeds because of p->tot_len check above. */
+ while ((q != NULL) && (q->len <= start)) {
+ start -= q->len;
+ q = q->next;
+ }
+
+ /* return requested data if pbuf is OK */
+ for (i = 0; i < n; i++) {
+ /* We know pbuf_get_at() succeeds because of p->tot_len check above. */
+ u8_t a = pbuf_get_at(q, start + i);
+ u8_t b = ((const u8_t*)s2)[i];
+ if (a != b) {
+ return i+1;
+ }
+ }
+ return 0;
+}
+
+/**
+ * @ingroup pbuf
+ * Find occurrence of mem (with length mem_len) in pbuf p, starting at offset
+ * start_offset.
+ *
+ * @param p pbuf to search, maximum length is 0xFFFE since 0xFFFF is used as
+ * return value 'not found'
+ * @param mem search for the contents of this buffer
+ * @param mem_len length of 'mem'
+ * @param start_offset offset into p at which to start searching
+ * @return 0xFFFF if substr was not found in p or the index where it was found
+ */
+u16_t
+pbuf_memfind(const struct pbuf* p, const void* mem, u16_t mem_len, u16_t start_offset)
+{
+ u16_t i;
+ u16_t max = p->tot_len - mem_len;
+ if (p->tot_len >= mem_len + start_offset) {
+ for (i = start_offset; i <= max; i++) {
+ u16_t plus = pbuf_memcmp(p, i, mem, mem_len);
+ if (plus == 0) {
+ return i;
+ }
+ }
+ }
+ return 0xFFFF;
+}
+
+/**
+ * Find occurrence of substr with length substr_len in pbuf p, start at offset
+ * start_offset
+ * WARNING: in contrast to strstr(), this one does not stop at the first \0 in
+ * the pbuf/source string!
+ *
+ * @param p pbuf to search, maximum length is 0xFFFE since 0xFFFF is used as
+ * return value 'not found'
+ * @param substr string to search for in p, maximum length is 0xFFFE
+ * @return 0xFFFF if substr was not found in p or the index where it was found
+ */
+u16_t
+pbuf_strstr(const struct pbuf* p, const char* substr)
+{
+ size_t substr_len;
+ if ((substr == NULL) || (substr[0] == 0) || (p->tot_len == 0xFFFF)) {
+ return 0xFFFF;
+ }
+ substr_len = strlen(substr);
+ if (substr_len >= 0xFFFF) {
+ return 0xFFFF;
+ }
+ return pbuf_memfind(p, substr, (u16_t)substr_len, 0);
+}
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/raw.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/raw.c
new file mode 100644
index 0000000..80cf9ec
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/raw.c
@@ -0,0 +1,521 @@
+/**
+ * @file
+ * Implementation of raw protocol PCBs for low-level handling of
+ * different types of protocols besides (or overriding) those
+ * already available in lwIP.\n
+ * See also @ref raw_raw
+ *
+ * @defgroup raw_raw RAW
+ * @ingroup callbackstyle_api
+ * Implementation of raw protocol PCBs for low-level handling of
+ * different types of protocols besides (or overriding) those
+ * already available in lwIP.\n
+ * @see @ref raw_api
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_RAW /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/def.h"
+#include "lwip/memp.h"
+#include "lwip/ip_addr.h"
+#include "lwip/netif.h"
+#include "lwip/raw.h"
+#include "lwip/stats.h"
+#include "lwip/ip6.h"
+#include "lwip/ip6_addr.h"
+#include "lwip/inet_chksum.h"
+
+#include <string.h>
+
+/** The list of RAW PCBs */
+static struct raw_pcb *raw_pcbs;
+
+static u8_t
+raw_input_match(struct raw_pcb *pcb, u8_t broadcast)
+{
+ LWIP_UNUSED_ARG(broadcast); /* in IPv6 only case */
+
+#if LWIP_IPV4 && LWIP_IPV6
+ /* Dual-stack: PCBs listening to any IP type also listen to any IP address */
+ if (IP_IS_ANY_TYPE_VAL(pcb->local_ip)) {
+#if IP_SOF_BROADCAST_RECV
+ if ((broadcast != 0) && !ip_get_option(pcb, SOF_BROADCAST)) {
+ return 0;
+ }
+#endif /* IP_SOF_BROADCAST_RECV */
+ return 1;
+ }
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+
+ /* Only need to check PCB if incoming IP version matches PCB IP version */
+ if (IP_ADDR_PCB_VERSION_MATCH_EXACT(pcb, ip_current_dest_addr())) {
+#if LWIP_IPV4
+ /* Special case: IPv4 broadcast: receive all broadcasts
+ * Note: broadcast variable can only be 1 if it is an IPv4 broadcast */
+ if (broadcast != 0) {
+#if IP_SOF_BROADCAST_RECV
+ if (ip_get_option(pcb, SOF_BROADCAST))
+#endif /* IP_SOF_BROADCAST_RECV */
+ {
+ if (ip4_addr_isany(ip_2_ip4(&pcb->local_ip))) {
+ return 1;
+ }
+ }
+ } else
+#endif /* LWIP_IPV4 */
+ /* Handle IPv4 and IPv6: catch all or exact match */
+ if (ip_addr_isany(&pcb->local_ip) ||
+ ip_addr_cmp(&pcb->local_ip, ip_current_dest_addr())) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Determine if in incoming IP packet is covered by a RAW PCB
+ * and if so, pass it to a user-provided receive callback function.
+ *
+ * Given an incoming IP datagram (as a chain of pbufs) this function
+ * finds a corresponding RAW PCB and calls the corresponding receive
+ * callback function.
+ *
+ * @param p pbuf to be demultiplexed to a RAW PCB.
+ * @param inp network interface on which the datagram was received.
+ * @return - 1 if the packet has been eaten by a RAW PCB receive
+ * callback function. The caller MAY NOT not reference the
+ * packet any longer, and MAY NOT call pbuf_free().
+ * @return - 0 if packet is not eaten (pbuf is still referenced by the
+ * caller).
+ *
+ */
+u8_t
+raw_input(struct pbuf *p, struct netif *inp)
+{
+ struct raw_pcb *pcb, *prev;
+ s16_t proto;
+ u8_t eaten = 0;
+ u8_t broadcast = ip_addr_isbroadcast(ip_current_dest_addr(), ip_current_netif());
+
+ LWIP_UNUSED_ARG(inp);
+
+#if LWIP_IPV6
+#if LWIP_IPV4
+ if (IP_HDR_GET_VERSION(p->payload) == 6)
+#endif /* LWIP_IPV4 */
+ {
+ struct ip6_hdr *ip6hdr = (struct ip6_hdr *)p->payload;
+ proto = IP6H_NEXTH(ip6hdr);
+ }
+#if LWIP_IPV4
+ else
+#endif /* LWIP_IPV4 */
+#endif /* LWIP_IPV6 */
+#if LWIP_IPV4
+ {
+ proto = IPH_PROTO((struct ip_hdr *)p->payload);
+ }
+#endif /* LWIP_IPV4 */
+
+ prev = NULL;
+ pcb = raw_pcbs;
+ /* loop through all raw pcbs until the packet is eaten by one */
+ /* this allows multiple pcbs to match against the packet by design */
+ while ((eaten == 0) && (pcb != NULL)) {
+ if ((pcb->protocol == proto) && raw_input_match(pcb, broadcast)) {
+ /* receive callback function available? */
+ if (pcb->recv != NULL) {
+#ifndef LWIP_NOASSERT
+ void* old_payload = p->payload;
+#endif
+ /* the receive callback function did not eat the packet? */
+ eaten = pcb->recv(pcb->recv_arg, pcb, p, ip_current_src_addr());
+ if (eaten != 0) {
+ /* receive function ate the packet */
+ p = NULL;
+ eaten = 1;
+ if (prev != NULL) {
+ /* move the pcb to the front of raw_pcbs so that is
+ found faster next time */
+ prev->next = pcb->next;
+ pcb->next = raw_pcbs;
+ raw_pcbs = pcb;
+ }
+ } else {
+ /* sanity-check that the receive callback did not alter the pbuf */
+ LWIP_ASSERT("raw pcb recv callback altered pbuf payload pointer without eating packet",
+ p->payload == old_payload);
+ }
+ }
+ /* no receive callback function was set for this raw PCB */
+ }
+ /* drop the packet */
+ prev = pcb;
+ pcb = pcb->next;
+ }
+ return eaten;
+}
+
+/**
+ * @ingroup raw_raw
+ * Bind a RAW PCB.
+ *
+ * @param pcb RAW PCB to be bound with a local address ipaddr.
+ * @param ipaddr local IP address to bind with. Use IP4_ADDR_ANY to
+ * bind to all local interfaces.
+ *
+ * @return lwIP error code.
+ * - ERR_OK. Successful. No error occurred.
+ * - ERR_USE. The specified IP address is already bound to by
+ * another RAW PCB.
+ *
+ * @see raw_disconnect()
+ */
+err_t
+raw_bind(struct raw_pcb *pcb, const ip_addr_t *ipaddr)
+{
+ if ((pcb == NULL) || (ipaddr == NULL)) {
+ return ERR_VAL;
+ }
+ ip_addr_set_ipaddr(&pcb->local_ip, ipaddr);
+ return ERR_OK;
+}
+
+/**
+ * @ingroup raw_raw
+ * Connect an RAW PCB. This function is required by upper layers
+ * of lwip. Using the raw api you could use raw_sendto() instead
+ *
+ * This will associate the RAW PCB with the remote address.
+ *
+ * @param pcb RAW PCB to be connected with remote address ipaddr and port.
+ * @param ipaddr remote IP address to connect with.
+ *
+ * @return lwIP error code
+ *
+ * @see raw_disconnect() and raw_sendto()
+ */
+err_t
+raw_connect(struct raw_pcb *pcb, const ip_addr_t *ipaddr)
+{
+ if ((pcb == NULL) || (ipaddr == NULL)) {
+ return ERR_VAL;
+ }
+ ip_addr_set_ipaddr(&pcb->remote_ip, ipaddr);
+ return ERR_OK;
+}
+
+/**
+ * @ingroup raw_raw
+ * Set the callback function for received packets that match the
+ * raw PCB's protocol and binding.
+ *
+ * The callback function MUST either
+ * - eat the packet by calling pbuf_free() and returning non-zero. The
+ * packet will not be passed to other raw PCBs or other protocol layers.
+ * - not free the packet, and return zero. The packet will be matched
+ * against further PCBs and/or forwarded to another protocol layers.
+ */
+void
+raw_recv(struct raw_pcb *pcb, raw_recv_fn recv, void *recv_arg)
+{
+ /* remember recv() callback and user data */
+ pcb->recv = recv;
+ pcb->recv_arg = recv_arg;
+}
+
+/**
+ * @ingroup raw_raw
+ * Send the raw IP packet to the given address. Note that actually you cannot
+ * modify the IP headers (this is inconsistent with the receive callback where
+ * you actually get the IP headers), you can only specify the IP payload here.
+ * It requires some more changes in lwIP. (there will be a raw_send() function
+ * then.)
+ *
+ * @param pcb the raw pcb which to send
+ * @param p the IP payload to send
+ * @param ipaddr the destination address of the IP packet
+ *
+ */
+err_t
+raw_sendto(struct raw_pcb *pcb, struct pbuf *p, const ip_addr_t *ipaddr)
+{
+ err_t err;
+ struct netif *netif;
+ const ip_addr_t *src_ip;
+ struct pbuf *q; /* q will be sent down the stack */
+ s16_t header_size;
+
+ if ((pcb == NULL) || (ipaddr == NULL) || !IP_ADDR_PCB_VERSION_MATCH(pcb, ipaddr)) {
+ return ERR_VAL;
+ }
+
+ LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_TRACE, ("raw_sendto\n"));
+
+ header_size = (
+#if LWIP_IPV4 && LWIP_IPV6
+ IP_IS_V6(ipaddr) ? IP6_HLEN : IP_HLEN);
+#elif LWIP_IPV4
+ IP_HLEN);
+#else
+ IP6_HLEN);
+#endif
+
+ /* not enough space to add an IP header to first pbuf in given p chain? */
+ if (pbuf_header(p, header_size)) {
+ /* allocate header in new pbuf */
+ q = pbuf_alloc(PBUF_IP, 0, PBUF_RAM);
+ /* new header pbuf could not be allocated? */
+ if (q == NULL) {
+ LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("raw_sendto: could not allocate header\n"));
+ return ERR_MEM;
+ }
+ if (p->tot_len != 0) {
+ /* chain header q in front of given pbuf p */
+ pbuf_chain(q, p);
+ }
+ /* { first pbuf q points to header pbuf } */
+ LWIP_DEBUGF(RAW_DEBUG, ("raw_sendto: added header pbuf %p before given pbuf %p\n", (void *)q, (void *)p));
+ } else {
+ /* first pbuf q equals given pbuf */
+ q = p;
+ if (pbuf_header(q, -header_size)) {
+ LWIP_ASSERT("Can't restore header we just removed!", 0);
+ return ERR_MEM;
+ }
+ }
+
+ if(IP_IS_ANY_TYPE_VAL(pcb->local_ip)) {
+ /* Don't call ip_route() with IP_ANY_TYPE */
+ netif = ip_route(IP46_ADDR_ANY(IP_GET_TYPE(ipaddr)), ipaddr);
+ } else {
+ netif = ip_route(&pcb->local_ip, ipaddr);
+ }
+
+ if (netif == NULL) {
+ LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_LEVEL_WARNING, ("raw_sendto: No route to "));
+ ip_addr_debug_print(RAW_DEBUG | LWIP_DBG_LEVEL_WARNING, ipaddr);
+ /* free any temporary header pbuf allocated by pbuf_header() */
+ if (q != p) {
+ pbuf_free(q);
+ }
+ return ERR_RTE;
+ }
+
+#if IP_SOF_BROADCAST
+ if (IP_IS_V4(ipaddr))
+ {
+ /* broadcast filter? */
+ if (!ip_get_option(pcb, SOF_BROADCAST) && ip_addr_isbroadcast(ipaddr, netif)) {
+ LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_LEVEL_WARNING, ("raw_sendto: SOF_BROADCAST not enabled on pcb %p\n", (void *)pcb));
+ /* free any temporary header pbuf allocated by pbuf_header() */
+ if (q != p) {
+ pbuf_free(q);
+ }
+ return ERR_VAL;
+ }
+ }
+#endif /* IP_SOF_BROADCAST */
+
+ if (ip_addr_isany(&pcb->local_ip)) {
+ /* use outgoing network interface IP address as source address */
+ src_ip = ip_netif_get_local_ip(netif, ipaddr);
+#if LWIP_IPV6
+ if (src_ip == NULL) {
+ if (q != p) {
+ pbuf_free(q);
+ }
+ return ERR_RTE;
+ }
+#endif /* LWIP_IPV6 */
+ } else {
+ /* use RAW PCB local IP address as source address */
+ src_ip = &pcb->local_ip;
+ }
+
+#if LWIP_IPV6
+ /* If requested, based on the IPV6_CHECKSUM socket option per RFC3542,
+ compute the checksum and update the checksum in the payload. */
+ if (IP_IS_V6(ipaddr) && pcb->chksum_reqd) {
+ u16_t chksum = ip6_chksum_pseudo(p, pcb->protocol, p->tot_len, ip_2_ip6(src_ip), ip_2_ip6(ipaddr));
+ LWIP_ASSERT("Checksum must fit into first pbuf", p->len >= (pcb->chksum_offset + 2));
+ SMEMCPY(((u8_t *)p->payload) + pcb->chksum_offset, &chksum, sizeof(u16_t));
+ }
+#endif
+
+ NETIF_SET_HWADDRHINT(netif, &pcb->addr_hint);
+ err = ip_output_if(q, src_ip, ipaddr, pcb->ttl, pcb->tos, pcb->protocol, netif);
+ NETIF_SET_HWADDRHINT(netif, NULL);
+
+ /* did we chain a header earlier? */
+ if (q != p) {
+ /* free the header */
+ pbuf_free(q);
+ }
+ return err;
+}
+
+/**
+ * @ingroup raw_raw
+ * Send the raw IP packet to the address given by raw_connect()
+ *
+ * @param pcb the raw pcb which to send
+ * @param p the IP payload to send
+ *
+ */
+err_t
+raw_send(struct raw_pcb *pcb, struct pbuf *p)
+{
+ return raw_sendto(pcb, p, &pcb->remote_ip);
+}
+
+/**
+ * @ingroup raw_raw
+ * Remove an RAW PCB.
+ *
+ * @param pcb RAW PCB to be removed. The PCB is removed from the list of
+ * RAW PCB's and the data structure is freed from memory.
+ *
+ * @see raw_new()
+ */
+void
+raw_remove(struct raw_pcb *pcb)
+{
+ struct raw_pcb *pcb2;
+ /* pcb to be removed is first in list? */
+ if (raw_pcbs == pcb) {
+ /* make list start at 2nd pcb */
+ raw_pcbs = raw_pcbs->next;
+ /* pcb not 1st in list */
+ } else {
+ for (pcb2 = raw_pcbs; pcb2 != NULL; pcb2 = pcb2->next) {
+ /* find pcb in raw_pcbs list */
+ if (pcb2->next != NULL && pcb2->next == pcb) {
+ /* remove pcb from list */
+ pcb2->next = pcb->next;
+ break;
+ }
+ }
+ }
+ memp_free(MEMP_RAW_PCB, pcb);
+}
+
+/**
+ * @ingroup raw_raw
+ * Create a RAW PCB.
+ *
+ * @return The RAW PCB which was created. NULL if the PCB data structure
+ * could not be allocated.
+ *
+ * @param proto the protocol number of the IPs payload (e.g. IP_PROTO_ICMP)
+ *
+ * @see raw_remove()
+ */
+struct raw_pcb *
+raw_new(u8_t proto)
+{
+ struct raw_pcb *pcb;
+
+ LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_TRACE, ("raw_new\n"));
+
+ pcb = (struct raw_pcb *)memp_malloc(MEMP_RAW_PCB);
+ /* could allocate RAW PCB? */
+ if (pcb != NULL) {
+ /* initialize PCB to all zeroes */
+ memset(pcb, 0, sizeof(struct raw_pcb));
+ pcb->protocol = proto;
+ pcb->ttl = RAW_TTL;
+ pcb->next = raw_pcbs;
+ raw_pcbs = pcb;
+ }
+ return pcb;
+}
+
+/**
+ * @ingroup raw_raw
+ * Create a RAW PCB for specific IP type.
+ *
+ * @return The RAW PCB which was created. NULL if the PCB data structure
+ * could not be allocated.
+ *
+ * @param type IP address type, see @ref lwip_ip_addr_type definitions.
+ * If you want to listen to IPv4 and IPv6 (dual-stack) packets,
+ * supply @ref IPADDR_TYPE_ANY as argument and bind to @ref IP_ANY_TYPE.
+ * @param proto the protocol number (next header) of the IPv6 packet payload
+ * (e.g. IP6_NEXTH_ICMP6)
+ *
+ * @see raw_remove()
+ */
+struct raw_pcb *
+raw_new_ip_type(u8_t type, u8_t proto)
+{
+ struct raw_pcb *pcb;
+ pcb = raw_new(proto);
+#if LWIP_IPV4 && LWIP_IPV6
+ if (pcb != NULL) {
+ IP_SET_TYPE_VAL(pcb->local_ip, type);
+ IP_SET_TYPE_VAL(pcb->remote_ip, type);
+ }
+#else /* LWIP_IPV4 && LWIP_IPV6 */
+ LWIP_UNUSED_ARG(type);
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+ return pcb;
+}
+
+/** This function is called from netif.c when address is changed
+ *
+ * @param old_addr IP address of the netif before change
+ * @param new_addr IP address of the netif after change
+ */
+void raw_netif_ip_addr_changed(const ip_addr_t* old_addr, const ip_addr_t* new_addr)
+{
+ struct raw_pcb* rpcb;
+
+ if (!ip_addr_isany(old_addr) && !ip_addr_isany(new_addr)) {
+ for (rpcb = raw_pcbs; rpcb != NULL; rpcb = rpcb->next) {
+ /* PCB bound to current local interface address? */
+ if (ip_addr_cmp(&rpcb->local_ip, old_addr)) {
+ /* The PCB is bound to the old ipaddr and
+ * is set to bound to the new one instead */
+ ip_addr_copy(rpcb->local_ip, *new_addr);
+ }
+ }
+ }
+}
+
+#endif /* LWIP_RAW */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/snmp/asn1_dec.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/snmp/asn1_dec.c
new file mode 100644
index 0000000..39b70bc
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/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 successful, 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, up to 64k
+ * @return ERR_OK if successful, 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 successful, 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 successful, 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 successful, 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 successful, 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/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/snmp/asn1_enc.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/snmp/asn1_enc.c
new file mode 100644
index 0000000..c1667e7
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/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, const 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 successful, 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 successful, 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 successful, 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 successful, 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 successful, 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, const 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 successful, 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/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/snmp/mib2.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/snmp/mib2.c
new file mode 100644
index 0000000..b6246f1
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/snmp/mib2.c
@@ -0,0 +1,4160 @@
+/**
+ * @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"
+
+#include <string.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 const struct snmp_obj_id sysobjid_default = {SNMP_SYSOBJID_LEN, SNMP_SYSOBJID};
+static const struct snmp_obj_id* sysobjid_ptr = &sysobjid_default;
+/** 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 const u8_t* sysdescr_len_ptr = &sysdescr_len_default;
+static const u8_t* sysdescr_ptr = &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;
+
+
+/**
+ * Initializes sysDescr pointers.
+ *
+ * @param str if non-NULL then copy str pointer
+ * @param len points to string length, excluding zero terminator
+ */
+void snmp_set_sysdescr(const u8_t *str, const u8_t *len)
+{
+ if (str != NULL)
+ {
+ sysdescr_ptr = str;
+ sysdescr_len_ptr = len;
+ }
+}
+
+void snmp_get_sysobjid_ptr(const struct snmp_obj_id **oid)
+{
+ *oid = sysobjid_ptr;
+}
+
+/**
+ * Initializes sysObjectID value.
+ *
+ * @param oid points to stuct snmp_obj_id to copy
+ */
+void snmp_set_sysobjid(const struct snmp_obj_id *oid)
+{
+ sysobjid_ptr = oid;
+}
+
+/**
+ * Must be called at regular 10 msec interval from a timer interrupt
+ * or signal handler depending on your runtime environment.
+ */
+void snmp_inc_sysuptime(void)
+{
+ sysuptime++;
+}
+
+void snmp_add_sysuptime(u32_t value)
+{
+ sysuptime+=value;
+}
+
+void snmp_get_sysuptime(u32_t *value)
+{
+ SNMP_GET_SYSUPTIME(sysuptime);
+ *value = sysuptime;
+}
+
+/**
+ * Initializes sysContact pointers,
+ * e.g. ptrs to non-volatile memory external to lwIP.
+ *
+ * @param ocstr if non-NULL then copy str pointer
+ * @param ocstrlen points to string length, excluding zero terminator
+ */
+void snmp_set_syscontact(u8_t *ocstr, u8_t *ocstrlen)
+{
+ if (ocstr != NULL)
+ {
+ syscontact_ptr = ocstr;
+ syscontact_len_ptr = ocstrlen;
+ }
+}
+
+/**
+ * Initializes sysName pointers,
+ * e.g. ptrs to non-volatile memory external to lwIP.
+ *
+ * @param ocstr if non-NULL then copy str pointer
+ * @param ocstrlen points to string length, excluding zero terminator
+ */
+void snmp_set_sysname(u8_t *ocstr, u8_t *ocstrlen)
+{
+ if (ocstr != NULL)
+ {
+ sysname_ptr = ocstr;
+ sysname_len_ptr = ocstrlen;
+ }
+}
+
+/**
+ * Initializes sysLocation pointers,
+ * e.g. ptrs to non-volatile memory external to lwIP.
+ *
+ * @param ocstr if non-NULL then copy str pointer
+ * @param ocstrlen points to string length, excluding zero terminator
+ */
+void snmp_set_syslocation(u8_t *ocstr, u8_t *ocstrlen)
+{
+ if (ocstr != NULL)
+ {
+ syslocation_ptr = ocstr;
+ syslocation_len_ptr = ocstrlen;
+ }
+}
+
+
+void snmp_add_ifinoctets(struct netif *ni, u32_t value)
+{
+ ni->ifinoctets += value;
+}
+
+void snmp_inc_ifinucastpkts(struct netif *ni)
+{
+ (ni->ifinucastpkts)++;
+}
+
+void snmp_inc_ifinnucastpkts(struct netif *ni)
+{
+ (ni->ifinnucastpkts)++;
+}
+
+void snmp_inc_ifindiscards(struct netif *ni)
+{
+ (ni->ifindiscards)++;
+}
+
+void snmp_add_ifoutoctets(struct netif *ni, u32_t value)
+{
+ ni->ifoutoctets += value;
+}
+
+void snmp_inc_ifoutucastpkts(struct netif *ni)
+{
+ (ni->ifoutucastpkts)++;
+}
+
+void snmp_inc_ifoutnucastpkts(struct netif *ni)
+{
+ (ni->ifoutnucastpkts)++;
+}
+
+void snmp_inc_ifoutdiscards(struct netif *ni)
+{
+ (ni->ifoutdiscards)++;
+}
+
+void snmp_inc_iflist(void)
+{
+ struct mib_list_node *if_node = NULL;
+
+ snmp_mib_node_insert(&iflist_root, iflist_root.count + 1, &if_node);
+ /* enable getnext traversal on filled table */
+ iftable.maxlength = 1;
+}
+
+void snmp_dec_iflist(void)
+{
+ snmp_mib_node_delete(&iflist_root, iflist_root.tail);
+ /* disable getnext traversal on empty table */
+ if(iflist_root.count == 0) iftable.maxlength = 0;
+}
+
+/**
+ * Inserts ARP table indexes (.xIfIndex.xNetAddress)
+ * into arp table index trees (both atTable and ipNetToMediaTable).
+ */
+void snmp_insert_arpidx_tree(struct netif *ni, ip_addr_t *ip)
+{
+ struct mib_list_rootnode *at_rn;
+ struct mib_list_node *at_node;
+ s32_t arpidx[5];
+ u8_t level, tree;
+
+ LWIP_ASSERT("ni != NULL", ni != NULL);
+ snmp_netiftoifindex(ni, &arpidx[0]);
+ snmp_iptooid(ip, &arpidx[1]);
+
+ for (tree = 0; tree < 2; tree++)
+ {
+ if (tree == 0)
+ {
+ at_rn = &arptree_root;
+ }
+ else
+ {
+ at_rn = &ipntomtree_root;
+ }
+ for (level = 0; level < 5; level++)
+ {
+ at_node = NULL;
+ snmp_mib_node_insert(at_rn, arpidx[level], &at_node);
+ if ((level != 4) && (at_node != NULL))
+ {
+ if (at_node->nptr == NULL)
+ {
+ at_rn = snmp_mib_lrn_alloc();
+ at_node->nptr = (struct mib_node*)at_rn;
+ if (at_rn != NULL)
+ {
+ if (level == 3)
+ {
+ if (tree == 0)
+ {
+ at_rn->get_object_def = atentry_get_object_def;
+ at_rn->get_value = atentry_get_value;
+ }
+ else
+ {
+ at_rn->get_object_def = ip_ntomentry_get_object_def;
+ at_rn->get_value = ip_ntomentry_get_value;
+ }
+ at_rn->set_test = noleafs_set_test;
+ at_rn->set_value = noleafs_set_value;
+ }
+ }
+ else
+ {
+ /* at_rn == NULL, malloc failure */
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmp_insert_arpidx_tree() insert failed, mem full"));
+ break;
+ }
+ }
+ else
+ {
+ at_rn = (struct mib_list_rootnode*)at_node->nptr;
+ }
+ }
+ }
+ }
+ /* enable getnext traversal on filled tables */
+ at.maxlength = 1;
+ ipntomtable.maxlength = 1;
+}
+
+/**
+ * Removes ARP table indexes (.xIfIndex.xNetAddress)
+ * from arp table index trees.
+ */
+void snmp_delete_arpidx_tree(struct netif *ni, ip_addr_t *ip)
+{
+ struct mib_list_rootnode *at_rn, *next, *del_rn[5];
+ struct mib_list_node *at_n, *del_n[5];
+ s32_t arpidx[5];
+ u8_t fc, tree, level, del_cnt;
+
+ snmp_netiftoifindex(ni, &arpidx[0]);
+ snmp_iptooid(ip, &arpidx[1]);
+
+ for (tree = 0; tree < 2; tree++)
+ {
+ /* mark nodes for deletion */
+ if (tree == 0)
+ {
+ at_rn = &arptree_root;
+ }
+ else
+ {
+ at_rn = &ipntomtree_root;
+ }
+ level = 0;
+ del_cnt = 0;
+ while ((level < 5) && (at_rn != NULL))
+ {
+ fc = snmp_mib_node_find(at_rn, arpidx[level], &at_n);
+ if (fc == 0)
+ {
+ /* arpidx[level] does not exist */
+ del_cnt = 0;
+ at_rn = NULL;
+ }
+ else if (fc == 1)
+ {
+ del_rn[del_cnt] = at_rn;
+ del_n[del_cnt] = at_n;
+ del_cnt++;
+ at_rn = (struct mib_list_rootnode*)(at_n->nptr);
+ }
+ else if (fc == 2)
+ {
+ /* reset delete (2 or more childs) */
+ del_cnt = 0;
+ at_rn = (struct mib_list_rootnode*)(at_n->nptr);
+ }
+ level++;
+ }
+ /* delete marked index nodes */
+ while (del_cnt > 0)
+ {
+ del_cnt--;
+
+ at_rn = del_rn[del_cnt];
+ at_n = del_n[del_cnt];
+
+ next = snmp_mib_node_delete(at_rn, at_n);
+ if (next != NULL)
+ {
+ LWIP_ASSERT("next_count == 0",next->count == 0);
+ snmp_mib_lrn_free(next);
+ }
+ }
+ }
+ /* disable getnext traversal on empty tables */
+ if(arptree_root.count == 0) at.maxlength = 0;
+ if(ipntomtree_root.count == 0) ipntomtable.maxlength = 0;
+}
+
+void snmp_inc_ipinreceives(void)
+{
+ ipinreceives++;
+}
+
+void snmp_inc_ipinhdrerrors(void)
+{
+ ipinhdrerrors++;
+}
+
+void snmp_inc_ipinaddrerrors(void)
+{
+ ipinaddrerrors++;
+}
+
+void snmp_inc_ipforwdatagrams(void)
+{
+ ipforwdatagrams++;
+}
+
+void snmp_inc_ipinunknownprotos(void)
+{
+ ipinunknownprotos++;
+}
+
+void snmp_inc_ipindiscards(void)
+{
+ ipindiscards++;
+}
+
+void snmp_inc_ipindelivers(void)
+{
+ ipindelivers++;
+}
+
+void snmp_inc_ipoutrequests(void)
+{
+ ipoutrequests++;
+}
+
+void snmp_inc_ipoutdiscards(void)
+{
+ ipoutdiscards++;
+}
+
+void snmp_inc_ipoutnoroutes(void)
+{
+ ipoutnoroutes++;
+}
+
+void snmp_inc_ipreasmreqds(void)
+{
+ ipreasmreqds++;
+}
+
+void snmp_inc_ipreasmoks(void)
+{
+ ipreasmoks++;
+}
+
+void snmp_inc_ipreasmfails(void)
+{
+ ipreasmfails++;
+}
+
+void snmp_inc_ipfragoks(void)
+{
+ ipfragoks++;
+}
+
+void snmp_inc_ipfragfails(void)
+{
+ ipfragfails++;
+}
+
+void snmp_inc_ipfragcreates(void)
+{
+ ipfragcreates++;
+}
+
+void snmp_inc_iproutingdiscards(void)
+{
+ iproutingdiscards++;
+}
+
+/**
+ * Inserts ipAddrTable indexes (.ipAdEntAddr)
+ * into index tree.
+ */
+void snmp_insert_ipaddridx_tree(struct netif *ni)
+{
+ struct mib_list_rootnode *ipa_rn;
+ struct mib_list_node *ipa_node;
+ s32_t ipaddridx[4];
+ u8_t level;
+
+ LWIP_ASSERT("ni != NULL", ni != NULL);
+ snmp_iptooid(&ni->ip_addr, &ipaddridx[0]);
+
+ level = 0;
+ ipa_rn = &ipaddrtree_root;
+ while (level < 4)
+ {
+ ipa_node = NULL;
+ snmp_mib_node_insert(ipa_rn, ipaddridx[level], &ipa_node);
+ if ((level != 3) && (ipa_node != NULL))
+ {
+ if (ipa_node->nptr == NULL)
+ {
+ ipa_rn = snmp_mib_lrn_alloc();
+ ipa_node->nptr = (struct mib_node*)ipa_rn;
+ if (ipa_rn != NULL)
+ {
+ if (level == 2)
+ {
+ ipa_rn->get_object_def = ip_addrentry_get_object_def;
+ ipa_rn->get_value = ip_addrentry_get_value;
+ ipa_rn->set_test = noleafs_set_test;
+ ipa_rn->set_value = noleafs_set_value;
+ }
+ }
+ else
+ {
+ /* ipa_rn == NULL, malloc failure */
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmp_insert_ipaddridx_tree() insert failed, mem full"));
+ break;
+ }
+ }
+ else
+ {
+ ipa_rn = (struct mib_list_rootnode*)ipa_node->nptr;
+ }
+ }
+ level++;
+ }
+ /* enable getnext traversal on filled table */
+ ipaddrtable.maxlength = 1;
+}
+
+/**
+ * Removes ipAddrTable indexes (.ipAdEntAddr)
+ * from index tree.
+ */
+void snmp_delete_ipaddridx_tree(struct netif *ni)
+{
+ struct mib_list_rootnode *ipa_rn, *next, *del_rn[4];
+ struct mib_list_node *ipa_n, *del_n[4];
+ s32_t ipaddridx[4];
+ u8_t fc, level, del_cnt;
+
+ LWIP_ASSERT("ni != NULL", ni != NULL);
+ snmp_iptooid(&ni->ip_addr, &ipaddridx[0]);
+
+ /* mark nodes for deletion */
+ level = 0;
+ del_cnt = 0;
+ ipa_rn = &ipaddrtree_root;
+ while ((level < 4) && (ipa_rn != NULL))
+ {
+ fc = snmp_mib_node_find(ipa_rn, ipaddridx[level], &ipa_n);
+ if (fc == 0)
+ {
+ /* ipaddridx[level] does not exist */
+ del_cnt = 0;
+ ipa_rn = NULL;
+ }
+ else if (fc == 1)
+ {
+ del_rn[del_cnt] = ipa_rn;
+ del_n[del_cnt] = ipa_n;
+ del_cnt++;
+ ipa_rn = (struct mib_list_rootnode*)(ipa_n->nptr);
+ }
+ else if (fc == 2)
+ {
+ /* reset delete (2 or more childs) */
+ del_cnt = 0;
+ ipa_rn = (struct mib_list_rootnode*)(ipa_n->nptr);
+ }
+ level++;
+ }
+ /* delete marked index nodes */
+ while (del_cnt > 0)
+ {
+ del_cnt--;
+
+ ipa_rn = del_rn[del_cnt];
+ ipa_n = del_n[del_cnt];
+
+ next = snmp_mib_node_delete(ipa_rn, ipa_n);
+ if (next != NULL)
+ {
+ LWIP_ASSERT("next_count == 0",next->count == 0);
+ snmp_mib_lrn_free(next);
+ }
+ }
+ /* disable getnext traversal on empty table */
+ if (ipaddrtree_root.count == 0) ipaddrtable.maxlength = 0;
+}
+
+/**
+ * Inserts ipRouteTable indexes (.ipRouteDest)
+ * into index tree.
+ *
+ * @param dflt non-zero for the default rte, zero for network rte
+ * @param ni points to network interface for this rte
+ *
+ * @todo record sysuptime for _this_ route when it is installed
+ * (needed for ipRouteAge) in the netif.
+ */
+void snmp_insert_iprteidx_tree(u8_t dflt, struct netif *ni)
+{
+ u8_t insert = 0;
+ ip_addr_t dst;
+
+ if (dflt != 0)
+ {
+ /* the default route 0.0.0.0 */
+ ip_addr_set_any(&dst);
+ insert = 1;
+ }
+ else
+ {
+ /* route to the network address */
+ ip_addr_get_network(&dst, &ni->ip_addr, &ni->netmask);
+ /* exclude 0.0.0.0 network (reserved for default rte) */
+ if (!ip_addr_isany(&dst)) {
+ insert = 1;
+ }
+ }
+ if (insert)
+ {
+ struct mib_list_rootnode *iprte_rn;
+ struct mib_list_node *iprte_node;
+ s32_t iprteidx[4];
+ u8_t level;
+
+ snmp_iptooid(&dst, &iprteidx[0]);
+ level = 0;
+ iprte_rn = &iprtetree_root;
+ while (level < 4)
+ {
+ iprte_node = NULL;
+ snmp_mib_node_insert(iprte_rn, iprteidx[level], &iprte_node);
+ if ((level != 3) && (iprte_node != NULL))
+ {
+ if (iprte_node->nptr == NULL)
+ {
+ iprte_rn = snmp_mib_lrn_alloc();
+ iprte_node->nptr = (struct mib_node*)iprte_rn;
+ if (iprte_rn != NULL)
+ {
+ if (level == 2)
+ {
+ iprte_rn->get_object_def = ip_rteentry_get_object_def;
+ iprte_rn->get_value = ip_rteentry_get_value;
+ iprte_rn->set_test = noleafs_set_test;
+ iprte_rn->set_value = noleafs_set_value;
+ }
+ }
+ else
+ {
+ /* iprte_rn == NULL, malloc failure */
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmp_insert_iprteidx_tree() insert failed, mem full"));
+ break;
+ }
+ }
+ else
+ {
+ iprte_rn = (struct mib_list_rootnode*)iprte_node->nptr;
+ }
+ }
+ level++;
+ }
+ }
+ /* enable getnext traversal on filled table */
+ iprtetable.maxlength = 1;
+}
+
+/**
+ * Removes ipRouteTable indexes (.ipRouteDest)
+ * from index tree.
+ *
+ * @param dflt non-zero for the default rte, zero for network rte
+ * @param ni points to network interface for this rte or NULL
+ * for default route to be removed.
+ */
+void snmp_delete_iprteidx_tree(u8_t dflt, struct netif *ni)
+{
+ u8_t del = 0;
+ ip_addr_t dst;
+
+ if (dflt != 0)
+ {
+ /* the default route 0.0.0.0 */
+ ip_addr_set_any(&dst);
+ del = 1;
+ }
+ else
+ {
+ /* route to the network address */
+ ip_addr_get_network(&dst, &ni->ip_addr, &ni->netmask);
+ /* exclude 0.0.0.0 network (reserved for default rte) */
+ if (!ip_addr_isany(&dst)) {
+ del = 1;
+ }
+ }
+ if (del)
+ {
+ struct mib_list_rootnode *iprte_rn, *next, *del_rn[4];
+ struct mib_list_node *iprte_n, *del_n[4];
+ s32_t iprteidx[4];
+ u8_t fc, level, del_cnt;
+
+ snmp_iptooid(&dst, &iprteidx[0]);
+ /* mark nodes for deletion */
+ level = 0;
+ del_cnt = 0;
+ iprte_rn = &iprtetree_root;
+ while ((level < 4) && (iprte_rn != NULL))
+ {
+ fc = snmp_mib_node_find(iprte_rn, iprteidx[level], &iprte_n);
+ if (fc == 0)
+ {
+ /* iprteidx[level] does not exist */
+ del_cnt = 0;
+ iprte_rn = NULL;
+ }
+ else if (fc == 1)
+ {
+ del_rn[del_cnt] = iprte_rn;
+ del_n[del_cnt] = iprte_n;
+ del_cnt++;
+ iprte_rn = (struct mib_list_rootnode*)(iprte_n->nptr);
+ }
+ else if (fc == 2)
+ {
+ /* reset delete (2 or more childs) */
+ del_cnt = 0;
+ iprte_rn = (struct mib_list_rootnode*)(iprte_n->nptr);
+ }
+ level++;
+ }
+ /* delete marked index nodes */
+ while (del_cnt > 0)
+ {
+ del_cnt--;
+
+ iprte_rn = del_rn[del_cnt];
+ iprte_n = del_n[del_cnt];
+
+ next = snmp_mib_node_delete(iprte_rn, iprte_n);
+ if (next != NULL)
+ {
+ LWIP_ASSERT("next_count == 0",next->count == 0);
+ snmp_mib_lrn_free(next);
+ }
+ }
+ }
+ /* disable getnext traversal on empty table */
+ if (iprtetree_root.count == 0) iprtetable.maxlength = 0;
+}
+
+
+void snmp_inc_icmpinmsgs(void)
+{
+ icmpinmsgs++;
+}
+
+void snmp_inc_icmpinerrors(void)
+{
+ icmpinerrors++;
+}
+
+void snmp_inc_icmpindestunreachs(void)
+{
+ icmpindestunreachs++;
+}
+
+void snmp_inc_icmpintimeexcds(void)
+{
+ icmpintimeexcds++;
+}
+
+void snmp_inc_icmpinparmprobs(void)
+{
+ icmpinparmprobs++;
+}
+
+void snmp_inc_icmpinsrcquenchs(void)
+{
+ icmpinsrcquenchs++;
+}
+
+void snmp_inc_icmpinredirects(void)
+{
+ icmpinredirects++;
+}
+
+void snmp_inc_icmpinechos(void)
+{
+ icmpinechos++;
+}
+
+void snmp_inc_icmpinechoreps(void)
+{
+ icmpinechoreps++;
+}
+
+void snmp_inc_icmpintimestamps(void)
+{
+ icmpintimestamps++;
+}
+
+void snmp_inc_icmpintimestampreps(void)
+{
+ icmpintimestampreps++;
+}
+
+void snmp_inc_icmpinaddrmasks(void)
+{
+ icmpinaddrmasks++;
+}
+
+void snmp_inc_icmpinaddrmaskreps(void)
+{
+ icmpinaddrmaskreps++;
+}
+
+void snmp_inc_icmpoutmsgs(void)
+{
+ icmpoutmsgs++;
+}
+
+void snmp_inc_icmpouterrors(void)
+{
+ icmpouterrors++;
+}
+
+void snmp_inc_icmpoutdestunreachs(void)
+{
+ icmpoutdestunreachs++;
+}
+
+void snmp_inc_icmpouttimeexcds(void)
+{
+ icmpouttimeexcds++;
+}
+
+void snmp_inc_icmpoutparmprobs(void)
+{
+ icmpoutparmprobs++;
+}
+
+void snmp_inc_icmpoutsrcquenchs(void)
+{
+ icmpoutsrcquenchs++;
+}
+
+void snmp_inc_icmpoutredirects(void)
+{
+ icmpoutredirects++;
+}
+
+void snmp_inc_icmpoutechos(void)
+{
+ icmpoutechos++;
+}
+
+void snmp_inc_icmpoutechoreps(void)
+{
+ icmpoutechoreps++;
+}
+
+void snmp_inc_icmpouttimestamps(void)
+{
+ icmpouttimestamps++;
+}
+
+void snmp_inc_icmpouttimestampreps(void)
+{
+ icmpouttimestampreps++;
+}
+
+void snmp_inc_icmpoutaddrmasks(void)
+{
+ icmpoutaddrmasks++;
+}
+
+void snmp_inc_icmpoutaddrmaskreps(void)
+{
+ icmpoutaddrmaskreps++;
+}
+
+void snmp_inc_tcpactiveopens(void)
+{
+ tcpactiveopens++;
+}
+
+void snmp_inc_tcppassiveopens(void)
+{
+ tcppassiveopens++;
+}
+
+void snmp_inc_tcpattemptfails(void)
+{
+ tcpattemptfails++;
+}
+
+void snmp_inc_tcpestabresets(void)
+{
+ tcpestabresets++;
+}
+
+void snmp_inc_tcpinsegs(void)
+{
+ tcpinsegs++;
+}
+
+void snmp_inc_tcpoutsegs(void)
+{
+ tcpoutsegs++;
+}
+
+void snmp_inc_tcpretranssegs(void)
+{
+ tcpretranssegs++;
+}
+
+void snmp_inc_tcpinerrs(void)
+{
+ tcpinerrs++;
+}
+
+void snmp_inc_tcpoutrsts(void)
+{
+ tcpoutrsts++;
+}
+
+void snmp_inc_udpindatagrams(void)
+{
+ udpindatagrams++;
+}
+
+void snmp_inc_udpnoports(void)
+{
+ udpnoports++;
+}
+
+void snmp_inc_udpinerrors(void)
+{
+ udpinerrors++;
+}
+
+void snmp_inc_udpoutdatagrams(void)
+{
+ udpoutdatagrams++;
+}
+
+/**
+ * Inserts udpTable indexes (.udpLocalAddress.udpLocalPort)
+ * into index tree.
+ */
+void snmp_insert_udpidx_tree(struct udp_pcb *pcb)
+{
+ struct mib_list_rootnode *udp_rn;
+ struct mib_list_node *udp_node;
+ s32_t udpidx[5];
+ u8_t level;
+
+ LWIP_ASSERT("pcb != NULL", pcb != NULL);
+ snmp_iptooid(ipX_2_ip(&pcb->local_ip), &udpidx[0]);
+ udpidx[4] = pcb->local_port;
+
+ udp_rn = &udp_root;
+ for (level = 0; level < 5; level++)
+ {
+ udp_node = NULL;
+ snmp_mib_node_insert(udp_rn, udpidx[level], &udp_node);
+ if ((level != 4) && (udp_node != NULL))
+ {
+ if (udp_node->nptr == NULL)
+ {
+ udp_rn = snmp_mib_lrn_alloc();
+ udp_node->nptr = (struct mib_node*)udp_rn;
+ if (udp_rn != NULL)
+ {
+ if (level == 3)
+ {
+ udp_rn->get_object_def = udpentry_get_object_def;
+ udp_rn->get_value = udpentry_get_value;
+ udp_rn->set_test = noleafs_set_test;
+ udp_rn->set_value = noleafs_set_value;
+ }
+ }
+ else
+ {
+ /* udp_rn == NULL, malloc failure */
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmp_insert_udpidx_tree() insert failed, mem full"));
+ break;
+ }
+ }
+ else
+ {
+ udp_rn = (struct mib_list_rootnode*)udp_node->nptr;
+ }
+ }
+ }
+ udptable.maxlength = 1;
+}
+
+/**
+ * Removes udpTable indexes (.udpLocalAddress.udpLocalPort)
+ * from index tree.
+ */
+void snmp_delete_udpidx_tree(struct udp_pcb *pcb)
+{
+ struct udp_pcb *npcb;
+ struct mib_list_rootnode *udp_rn, *next, *del_rn[5];
+ struct mib_list_node *udp_n, *del_n[5];
+ s32_t udpidx[5];
+ u8_t bindings, fc, level, del_cnt;
+
+ LWIP_ASSERT("pcb != NULL", pcb != NULL);
+ snmp_iptooid(ipX_2_ip(&pcb->local_ip), &udpidx[0]);
+ udpidx[4] = pcb->local_port;
+
+ /* count PCBs for a given binding
+ (e.g. when reusing ports or for temp output PCBs) */
+ bindings = 0;
+ npcb = udp_pcbs;
+ while ((npcb != NULL))
+ {
+ if (ipX_addr_cmp(0, &npcb->local_ip, &pcb->local_ip) &&
+ (npcb->local_port == udpidx[4]))
+ {
+ bindings++;
+ }
+ npcb = npcb->next;
+ }
+ if (bindings == 1)
+ {
+ /* selectively remove */
+ /* mark nodes for deletion */
+ level = 0;
+ del_cnt = 0;
+ udp_rn = &udp_root;
+ while ((level < 5) && (udp_rn != NULL))
+ {
+ fc = snmp_mib_node_find(udp_rn, udpidx[level], &udp_n);
+ if (fc == 0)
+ {
+ /* udpidx[level] does not exist */
+ del_cnt = 0;
+ udp_rn = NULL;
+ }
+ else if (fc == 1)
+ {
+ del_rn[del_cnt] = udp_rn;
+ del_n[del_cnt] = udp_n;
+ del_cnt++;
+ udp_rn = (struct mib_list_rootnode*)(udp_n->nptr);
+ }
+ else if (fc == 2)
+ {
+ /* reset delete (2 or more childs) */
+ del_cnt = 0;
+ udp_rn = (struct mib_list_rootnode*)(udp_n->nptr);
+ }
+ level++;
+ }
+ /* delete marked index nodes */
+ while (del_cnt > 0)
+ {
+ del_cnt--;
+
+ udp_rn = del_rn[del_cnt];
+ udp_n = del_n[del_cnt];
+
+ next = snmp_mib_node_delete(udp_rn, udp_n);
+ if (next != NULL)
+ {
+ LWIP_ASSERT("next_count == 0",next->count == 0);
+ snmp_mib_lrn_free(next);
+ }
+ }
+ }
+ /* disable getnext traversal on empty table */
+ if (udp_root.count == 0) udptable.maxlength = 0;
+}
+
+
+void snmp_inc_snmpinpkts(void)
+{
+ snmpinpkts++;
+}
+
+void snmp_inc_snmpoutpkts(void)
+{
+ snmpoutpkts++;
+}
+
+void snmp_inc_snmpinbadversions(void)
+{
+ snmpinbadversions++;
+}
+
+void snmp_inc_snmpinbadcommunitynames(void)
+{
+ snmpinbadcommunitynames++;
+}
+
+void snmp_inc_snmpinbadcommunityuses(void)
+{
+ snmpinbadcommunityuses++;
+}
+
+void snmp_inc_snmpinasnparseerrs(void)
+{
+ snmpinasnparseerrs++;
+}
+
+void snmp_inc_snmpintoobigs(void)
+{
+ snmpintoobigs++;
+}
+
+void snmp_inc_snmpinnosuchnames(void)
+{
+ snmpinnosuchnames++;
+}
+
+void snmp_inc_snmpinbadvalues(void)
+{
+ snmpinbadvalues++;
+}
+
+void snmp_inc_snmpinreadonlys(void)
+{
+ snmpinreadonlys++;
+}
+
+void snmp_inc_snmpingenerrs(void)
+{
+ snmpingenerrs++;
+}
+
+void snmp_add_snmpintotalreqvars(u8_t value)
+{
+ snmpintotalreqvars += value;
+}
+
+void snmp_add_snmpintotalsetvars(u8_t value)
+{
+ snmpintotalsetvars += value;
+}
+
+void snmp_inc_snmpingetrequests(void)
+{
+ snmpingetrequests++;
+}
+
+void snmp_inc_snmpingetnexts(void)
+{
+ snmpingetnexts++;
+}
+
+void snmp_inc_snmpinsetrequests(void)
+{
+ snmpinsetrequests++;
+}
+
+void snmp_inc_snmpingetresponses(void)
+{
+ snmpingetresponses++;
+}
+
+void snmp_inc_snmpintraps(void)
+{
+ snmpintraps++;
+}
+
+void snmp_inc_snmpouttoobigs(void)
+{
+ snmpouttoobigs++;
+}
+
+void snmp_inc_snmpoutnosuchnames(void)
+{
+ snmpoutnosuchnames++;
+}
+
+void snmp_inc_snmpoutbadvalues(void)
+{
+ snmpoutbadvalues++;
+}
+
+void snmp_inc_snmpoutgenerrs(void)
+{
+ snmpoutgenerrs++;
+}
+
+void snmp_inc_snmpoutgetrequests(void)
+{
+ snmpoutgetrequests++;
+}
+
+void snmp_inc_snmpoutgetnexts(void)
+{
+ snmpoutgetnexts++;
+}
+
+void snmp_inc_snmpoutsetrequests(void)
+{
+ snmpoutsetrequests++;
+}
+
+void snmp_inc_snmpoutgetresponses(void)
+{
+ snmpoutgetresponses++;
+}
+
+void snmp_inc_snmpouttraps(void)
+{
+ snmpouttraps++;
+}
+
+void snmp_get_snmpgrpid_ptr(const 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_ptr->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 */
+ MEMCPY(value, sysdescr_ptr, len);
+ break;
+ case 2: /* sysObjectID */
+ MEMCPY(value, sysobjid_ptr->id, len);
+ break;
+ case 3: /* sysUpTime */
+ {
+ snmp_get_sysuptime((u32_t*)value);
+ }
+ break;
+ case 4: /* sysContact */
+ MEMCPY(value, syscontact_ptr, len);
+ break;
+ case 5: /* sysName */
+ MEMCPY(value, sysname_ptr, len);
+ break;
+ case 6: /* sysLocation */
+ MEMCPY(value, syslocation_ptr, len);
+ break;
+ case 7: /* sysServices */
+ {
+ s32_t *sint_ptr = (s32_t*)value;
+ *sint_ptr = sysservices;
+ }
+ break;
+ default:
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("system_get_value(): unknown id: %d\n", id));
+ 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;
+ default:
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("system_set_test(): unknown id: %d\n", id));
+ 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 */
+ MEMCPY(syscontact_ptr, value, len);
+ *syscontact_len_ptr = (u8_t)len;
+ break;
+ case 5: /* sysName */
+ MEMCPY(sysname_ptr, value, len);
+ *sysname_len_ptr = (u8_t)len;
+ break;
+ case 6: /* sysLocation */
+ MEMCPY(syslocation_ptr, value, len);
+ *syslocation_len_ptr = (u8_t)len;
+ break;
+ default:
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("system_set_value(): unknown id: %d\n", id));
+ 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 */
+ MEMCPY(value, 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 */
+ MEMCPY(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 */
+ MEMCPY(value, ifspecific.id, len);
+ break;
+ default:
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("ifentry_get_value(): unknown id: %d\n", id));
+ 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;
+ const 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;
+ default:
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("atentry_get_value(): unknown id: %d\n", id));
+ 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;
+ default:
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_get_value(): unknown id: %d\n", id));
+ 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;
+ default:
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_set_test(): unknown id: %d\n", id));
+ 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;
+ default:
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_addrentry_get_value(): unknown id: %d\n", id));
+ 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 */
+ MEMCPY(value, iprouteinfo.id, len);
+ break;
+ default:
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_rteentry_get_value(): unknown id: %d\n", id));
+ 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;
+ const 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;
+ default:
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_ntomentry_get_value(): unknown id: %d\n", id));
+ 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;
+ default:
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("icmp_get_value(): unknown id: %d\n", id));
+ 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;
+ default:
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("tcp_get_value(): unknown id: %d\n", id));
+ 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;
+ default:
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("udp_get_value(): unknown id: %d\n", id));
+ break;
+ }
+}
+
+static void
+udpentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od)
+{
+ /* return to object name, adding index depth (5) */
+ ident_len += 5;
+ ident -= 5;
+
+ if (ident_len == 6)
+ {
+ od->id_inst_len = ident_len;
+ od->id_inst_ptr = ident;
+
+ switch (ident[0])
+ {
+ case 1: /* udpLocalAddress */
+ od->instance = MIB_OBJECT_TAB;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR);
+ od->v_len = 4;
+ break;
+ case 2: /* udpLocalPort */
+ od->instance = MIB_OBJECT_TAB;
+ od->access = MIB_OBJECT_READ_ONLY;
+ od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG);
+ od->v_len = sizeof(s32_t);
+ break;
+ default:
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("udpentry_get_object_def: no such object\n"));
+ od->instance = MIB_OBJECT_NONE;
+ break;
+ }
+ }
+ else
+ {
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("udpentry_get_object_def: no scalar\n"));
+ od->instance = MIB_OBJECT_NONE;
+ }
+}
+
+static void
+udpentry_get_value(struct obj_def *od, u16_t len, void *value)
+{
+ u8_t id;
+ struct udp_pcb *pcb;
+ ipX_addr_t ip;
+ u16_t port;
+
+ LWIP_UNUSED_ARG(len);
+ snmp_oidtoip(&od->id_inst_ptr[1], (ip_addr_t*)&ip);
+ LWIP_ASSERT("invalid port", (od->id_inst_ptr[5] >= 0) && (od->id_inst_ptr[5] <= 0xffff));
+ port = (u16_t)od->id_inst_ptr[5];
+
+ pcb = udp_pcbs;
+ while ((pcb != NULL) &&
+ !(ipX_addr_cmp(0, &pcb->local_ip, &ip) &&
+ (pcb->local_port == port)))
+ {
+ pcb = pcb->next;
+ }
+
+ if (pcb != NULL)
+ {
+ LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff));
+ id = (u8_t)od->id_inst_ptr[0];
+ switch (id)
+ {
+ case 1: /* udpLocalAddress */
+ {
+ ipX_addr_t *dst = (ipX_addr_t*)value;
+ ipX_addr_copy(0, *dst, pcb->local_ip);
+ }
+ break;
+ case 2: /* udpLocalPort */
+ {
+ s32_t *sint_ptr = (s32_t*)value;
+ *sint_ptr = pcb->local_port;
+ }
+ break;
+ default:
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("udpentry_get_value(): unknown id: %d\n", id));
+ 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;
+ default:
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmp_get_value(): unknown id: %d\n", id));
+ 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/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/snmp/mib_structs.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/snmp/mib_structs.c
new file mode 100644
index 0000000..fa796c5
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/snmp/mib_structs.c
@@ -0,0 +1,1175 @@
+/**
+ * @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];
+const struct nse node_null = {NULL, 0, 0};
+
+/**
+ * Pushes nse struct onto stack.
+ */
+static void
+push_node(const 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
+ {
+ u16_t j;
+
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("non-leaf node\n"));
+ /* non-leaf, store right child ptr and id */
+ LWIP_ASSERT("i < 0xff", i < 0xff);
+ j = i + 1;
+ while ((j < an->maxlength) && (empty_table(an->nptr[j])))
+ {
+ j++;
+ }
+ if (j < an->maxlength)
+ {
+ struct nse cur_node;
+ cur_node.r_ptr = an->nptr[j];
+ cur_node.r_id = an->objid[j];
+ cur_node.r_nl = 0;
+ push_node(&cur_node);
+ }
+ else
+ {
+ push_node(&node_null);
+ }
+ 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
+ {
+ u16_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;
+
+ /* non-leaf, store right child ptr and id */
+ jn = ln->next;
+ while ((jn != NULL) && empty_table(jn->nptr))
+ {
+ jn = jn->next;
+ }
+ if (jn != NULL)
+ {
+ struct nse cur_node;
+ cur_node.r_ptr = jn->nptr;
+ cur_node.r_id = jn->objid;
+ cur_node.r_nl = 0;
+ push_node(&cur_node);
+ }
+ else
+ {
+ push_node(&node_null);
+ }
+ 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
+ {
+ u16_t j;
+
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("non-leaf node\n"));
+ /* non-leaf, store right child ptr and id */
+ LWIP_ASSERT("i < 0xff", i < 0xff);
+ j = i + 1;
+ if (j < len)
+ {
+ struct nse cur_node;
+ /* 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;
+ push_node(&cur_node);
+ }
+ else
+ {
+ push_node(&node_null);
+ }
+ 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/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/snmp/msg_in.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/snmp/msg_in.c
new file mode 100644
index 0000000..57457b5
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/snmp/msg_in.c
@@ -0,0 +1,1555 @@
+/**
+ * @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;
+/** SNMP community string */
+const char *snmp_community = SNMP_COMMUNITY;
+#if SNMP_COMMUNITY_EXT
+/** SNMP community string for write access */
+const char *snmp_community_write = SNMP_COMMUNITY_WRITE;
+/** SNMP community string for sending traps */
+const char *snmp_community_trap = SNMP_COMMUNITY_TRAP;
+#endif /* SNMP_COMMUNITY_EXT */
+
+/* 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, const 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();
+}
+
+/**
+ * Returns current SNMP community string.
+ * @return current SNMP community string
+ */
+const char *
+snmp_get_community(void)
+{
+ return snmp_community;
+}
+
+/**
+ * Sets SNMP community string.
+ * The string itself (its storage) must be valid throughout the whole life of
+ * program (or until it is changed to sth else).
+ *
+ * @param community is a pointer to new community string
+ */
+void
+snmp_set_community(const char * const community)
+{
+ LWIP_ASSERT("community string is too long!", strlen(community) <= SNMP_COMMUNITY_STR_LEN);
+ snmp_community = community;
+}
+
+#if SNMP_COMMUNITY_EXT
+/**
+ * Returns current SNMP write-access community string.
+ * @return current SNMP write-access community string
+ */
+const char *
+snmp_get_community_write(void)
+{
+ return snmp_community_write;
+}
+
+/**
+ * Returns current SNMP community string used for sending traps.
+ * @return current SNMP community string used for sending traps
+ */
+const char *
+snmp_get_community_trap(void)
+{
+ return snmp_community_trap;
+}
+
+/**
+ * Sets SNMP community string for write-access.
+ * The string itself (its storage) must be valid throughout the whole life of
+ * program (or until it is changed to sth else).
+ *
+ * @param community is a pointer to new write-access community string
+ */
+void
+snmp_set_community_write(const char * const community)
+{
+ LWIP_ASSERT("community string is too long!", strlen(community) <= SNMP_COMMUNITY_STR_LEN);
+ snmp_community_write = community;
+}
+
+/**
+ * Sets SNMP community string used for sending traps.
+ * The string itself (its storage) must be valid throughout the whole life of
+ * program (or until it is changed to sth else).
+ *
+ * @param community is a pointer to new trap community string
+ */
+void
+snmp_set_community_trap(const char * const community)
+{
+ LWIP_ASSERT("community string is too long!", strlen(community) <= SNMP_COMMUNITY_STR_LEN);
+ snmp_community_trap = community;
+}
+#endif /* SNMP_COMMUNITY_EXT */
+
+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++) {
+ if (vbi->ident != NULL) {
+ /* free previously allocated value before overwriting the pointer */
+ memp_free(MEMP_SNMP_VALUE, vbi->ident);
+ }
+ 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 associated 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->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 referenced 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)
+ {
+ 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 referenced 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 associated 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 associated 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, const 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;
+ ofs += (1 + len_octets + len);
+ snmp_asn1_dec_type(p, ofs, &type);
+#if SNMP_COMMUNITY_EXT
+ if (strncmp(snmp_community_write, (const char*)m_stat->community, SNMP_COMMUNITY_STR_LEN) != 0)
+ {
+ /* community does not match the write-access community, check if this is a SetRequest */
+ if (type == (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_SET_REQ))
+ {
+ /* wrong community for SetRequest PDU */
+ snmp_inc_snmpinbadcommunitynames();
+ snmp_authfail_trap();
+ return ERR_ARG;
+ }
+#else /* SNMP_COMMUNITY_EXT */
+ {
+#endif /* SNMP_COMMUNITY_EXT */
+ if (strncmp(snmp_community, (const char*)m_stat->community, SNMP_COMMUNITY_STR_LEN) != 0)
+ {
+ snmp_inc_snmpinbadcommunitynames();
+ snmp_authfail_trap();
+ return ERR_ARG;
+ }
+ }
+ 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_NOERROR:
+ /* nothing to do */
+ break;
+ 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;
+ default:
+ LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_pdu_header_check(): unknown error_status: %d\n", (int)m_stat->error_status));
+ 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/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/snmp/msg_out.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/snmp/msg_out.c
new file mode 100644
index 0000000..ff74e50
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/snmp/msg_out.c
@@ -0,0 +1,690 @@
+/**
+ * @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"
+
+#include <string.h>
+
+#if !SNMP_COMMUNITY_EXT
+#define snmp_community_trap snmp_community
+#endif
+
+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_NOERROR:
+ /* nothing to do */
+ break;
+ 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;
+ default:
+ LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_send_response(): unknown error_status: %d\n", (int)m_stat->error_status));
+ 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 enterprise 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, const struct snmp_obj_id *eoid, s32_t specific_trap)
+{
+ struct snmp_trap_dst *td;
+ struct netif *dst_if;
+ ip_addr_t dst_ip;
+ struct pbuf *p;
+ u16_t i,tot_len;
+ err_t err = ERR_OK;
+
+ for (i=0, td = &trap_dst[0]; 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);
+ if (dst_if != NULL) {
+ ip_addr_copy(dst_ip, dst_if->ip_addr);
+ /* @todo: what about IPv6? */
+ trap_msg.sip_raw[0] = ip4_addr1(&dst_ip);
+ trap_msg.sip_raw[1] = ip4_addr2(&dst_ip);
+ trap_msg.sip_raw[2] = ip4_addr3(&dst_ip);
+ trap_msg.sip_raw[3] = ip4_addr4(&dst_ip);
+ trap_msg.gen_trap = generic_trap;
+ trap_msg.spc_trap = specific_trap;
+ if (generic_trap == SNMP_GENTRAP_ENTERPRISESPC)
+ {
+ /* enterprise-Specific trap */
+ trap_msg.enterprise = eoid;
+ }
+ else
+ {
+ /* generic (MIB-II) trap */
+ snmp_get_snmpgrpid_ptr(&trap_msg.enterprise);
+ }
+ snmp_get_sysuptime(&trap_msg.ts);
+
+ /* pass 0, calculate length fields */
+ tot_len = snmp_varbind_list_sum(&trap_msg.outvb);
+ tot_len = snmp_trap_header_sum(&trap_msg, tot_len);
+
+ /* allocate pbuf(s) */
+ p = pbuf_alloc(PBUF_TRANSPORT, tot_len, PBUF_POOL);
+ if (p != NULL)
+ {
+ u16_t ofs;
+
+ /* pass 1, encode packet ino the pbuf(s) */
+ ofs = snmp_trap_header_enc(&trap_msg, p);
+ snmp_varbind_list_enc(&trap_msg.outvb, p, ofs);
+
+ snmp_inc_snmpouttraps();
+ snmp_inc_snmpoutpkts();
+
+ /** send to the TRAP destination */
+ udp_sendto(trap_msg.pcb, p, &trap_msg.dip, SNMP_TRAP_PORT);
+
+ pbuf_free(p);
+ } else {
+ err = ERR_MEM;
+ }
+ } else {
+ /* routing error */
+ err = ERR_RTE;
+ }
+ }
+ }
+ return err;
+}
+
+void
+snmp_coldstart_trap(void)
+{
+ trap_msg.outvb.head = NULL;
+ trap_msg.outvb.tail = NULL;
+ trap_msg.outvb.count = 0;
+ snmp_send_trap(SNMP_GENTRAP_COLDSTART, NULL, 0);
+}
+
+void
+snmp_authfail_trap(void)
+{
+ u8_t enable;
+ snmp_get_snmpenableauthentraps(&enable);
+ if (enable == 1)
+ {
+ trap_msg.outvb.head = NULL;
+ trap_msg.outvb.tail = NULL;
+ trap_msg.outvb.count = 0;
+ snmp_send_trap(SNMP_GENTRAP_AUTHFAIL, NULL, 0);
+ }
+}
+
+/**
+ * Sums response header field lengths from tail to head and
+ * returns resp_header_lengths for second encoding pass.
+ *
+ * @param vb_len varbind-list length
+ * @param rhl points to returned header lengths
+ * @return the required length 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 length 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 = strlen(snmp_community_trap);
+ 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 length 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_community_trap[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/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/stats.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/stats.c
new file mode 100644
index 0000000..893d199
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/stats.c
@@ -0,0 +1,169 @@
+/**
+ * @file
+ * Statistics module
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_STATS /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/def.h"
+#include "lwip/stats.h"
+#include "lwip/mem.h"
+#include "lwip/debug.h"
+
+#include <string.h>
+
+struct stats_ lwip_stats;
+
+void
+stats_init(void)
+{
+#ifdef LWIP_DEBUG
+#if MEM_STATS
+ lwip_stats.mem.name = "MEM";
+#endif /* MEM_STATS */
+#endif /* LWIP_DEBUG */
+}
+
+#if LWIP_STATS_DISPLAY
+void
+stats_display_proto(struct stats_proto *proto, const char *name)
+{
+ LWIP_PLATFORM_DIAG(("\n%s\n\t", name));
+ LWIP_PLATFORM_DIAG(("xmit: %"STAT_COUNTER_F"\n\t", proto->xmit));
+ LWIP_PLATFORM_DIAG(("recv: %"STAT_COUNTER_F"\n\t", proto->recv));
+ LWIP_PLATFORM_DIAG(("fw: %"STAT_COUNTER_F"\n\t", proto->fw));
+ LWIP_PLATFORM_DIAG(("drop: %"STAT_COUNTER_F"\n\t", proto->drop));
+ LWIP_PLATFORM_DIAG(("chkerr: %"STAT_COUNTER_F"\n\t", proto->chkerr));
+ LWIP_PLATFORM_DIAG(("lenerr: %"STAT_COUNTER_F"\n\t", proto->lenerr));
+ LWIP_PLATFORM_DIAG(("memerr: %"STAT_COUNTER_F"\n\t", proto->memerr));
+ LWIP_PLATFORM_DIAG(("rterr: %"STAT_COUNTER_F"\n\t", proto->rterr));
+ LWIP_PLATFORM_DIAG(("proterr: %"STAT_COUNTER_F"\n\t", proto->proterr));
+ LWIP_PLATFORM_DIAG(("opterr: %"STAT_COUNTER_F"\n\t", proto->opterr));
+ LWIP_PLATFORM_DIAG(("err: %"STAT_COUNTER_F"\n\t", proto->err));
+ LWIP_PLATFORM_DIAG(("cachehit: %"STAT_COUNTER_F"\n", proto->cachehit));
+}
+
+#if IGMP_STATS || MLD6_STATS
+void
+stats_display_igmp(struct stats_igmp *igmp, const char *name)
+{
+ LWIP_PLATFORM_DIAG(("\n%s\n\t", name));
+ LWIP_PLATFORM_DIAG(("xmit: %"STAT_COUNTER_F"\n\t", igmp->xmit));
+ LWIP_PLATFORM_DIAG(("recv: %"STAT_COUNTER_F"\n\t", igmp->recv));
+ LWIP_PLATFORM_DIAG(("drop: %"STAT_COUNTER_F"\n\t", igmp->drop));
+ LWIP_PLATFORM_DIAG(("chkerr: %"STAT_COUNTER_F"\n\t", igmp->chkerr));
+ LWIP_PLATFORM_DIAG(("lenerr: %"STAT_COUNTER_F"\n\t", igmp->lenerr));
+ LWIP_PLATFORM_DIAG(("memerr: %"STAT_COUNTER_F"\n\t", igmp->memerr));
+ LWIP_PLATFORM_DIAG(("proterr: %"STAT_COUNTER_F"\n\t", igmp->proterr));
+ LWIP_PLATFORM_DIAG(("rx_v1: %"STAT_COUNTER_F"\n\t", igmp->rx_v1));
+ LWIP_PLATFORM_DIAG(("rx_group: %"STAT_COUNTER_F"\n\t", igmp->rx_group));
+ LWIP_PLATFORM_DIAG(("rx_general: %"STAT_COUNTER_F"\n\t", igmp->rx_general));
+ LWIP_PLATFORM_DIAG(("rx_report: %"STAT_COUNTER_F"\n\t", igmp->rx_report));
+ LWIP_PLATFORM_DIAG(("tx_join: %"STAT_COUNTER_F"\n\t", igmp->tx_join));
+ LWIP_PLATFORM_DIAG(("tx_leave: %"STAT_COUNTER_F"\n\t", igmp->tx_leave));
+ LWIP_PLATFORM_DIAG(("tx_report: %"STAT_COUNTER_F"\n", igmp->tx_report));
+}
+#endif /* IGMP_STATS || MLD6_STATS */
+
+#if MEM_STATS || MEMP_STATS
+void
+stats_display_mem(struct stats_mem *mem, const char *name)
+{
+ LWIP_PLATFORM_DIAG(("\nMEM %s\n\t", name));
+ LWIP_PLATFORM_DIAG(("avail: %"U32_F"\n\t", (u32_t)mem->avail));
+ LWIP_PLATFORM_DIAG(("used: %"U32_F"\n\t", (u32_t)mem->used));
+ LWIP_PLATFORM_DIAG(("max: %"U32_F"\n\t", (u32_t)mem->max));
+ LWIP_PLATFORM_DIAG(("err: %"U32_F"\n", (u32_t)mem->err));
+}
+
+#if MEMP_STATS
+void
+stats_display_memp(struct stats_mem *mem, int index)
+{
+ if (index < MEMP_MAX) {
+ stats_display_mem(mem, mem->name);
+ }
+}
+#endif /* MEMP_STATS */
+#endif /* MEM_STATS || MEMP_STATS */
+
+#if SYS_STATS
+void
+stats_display_sys(struct stats_sys *sys)
+{
+ LWIP_PLATFORM_DIAG(("\nSYS\n\t"));
+ LWIP_PLATFORM_DIAG(("sem.used: %"U32_F"\n\t", (u32_t)sys->sem.used));
+ LWIP_PLATFORM_DIAG(("sem.max: %"U32_F"\n\t", (u32_t)sys->sem.max));
+ LWIP_PLATFORM_DIAG(("sem.err: %"U32_F"\n\t", (u32_t)sys->sem.err));
+ LWIP_PLATFORM_DIAG(("mutex.used: %"U32_F"\n\t", (u32_t)sys->mutex.used));
+ LWIP_PLATFORM_DIAG(("mutex.max: %"U32_F"\n\t", (u32_t)sys->mutex.max));
+ LWIP_PLATFORM_DIAG(("mutex.err: %"U32_F"\n\t", (u32_t)sys->mutex.err));
+ LWIP_PLATFORM_DIAG(("mbox.used: %"U32_F"\n\t", (u32_t)sys->mbox.used));
+ LWIP_PLATFORM_DIAG(("mbox.max: %"U32_F"\n\t", (u32_t)sys->mbox.max));
+ LWIP_PLATFORM_DIAG(("mbox.err: %"U32_F"\n", (u32_t)sys->mbox.err));
+}
+#endif /* SYS_STATS */
+
+void
+stats_display(void)
+{
+ s16_t i;
+
+ LINK_STATS_DISPLAY();
+ ETHARP_STATS_DISPLAY();
+ IPFRAG_STATS_DISPLAY();
+ IP6_FRAG_STATS_DISPLAY();
+ IP_STATS_DISPLAY();
+ ND6_STATS_DISPLAY();
+ IP6_STATS_DISPLAY();
+ IGMP_STATS_DISPLAY();
+ MLD6_STATS_DISPLAY();
+ ICMP_STATS_DISPLAY();
+ ICMP6_STATS_DISPLAY();
+ UDP_STATS_DISPLAY();
+ TCP_STATS_DISPLAY();
+ MEM_STATS_DISPLAY();
+ for (i = 0; i < MEMP_MAX; i++) {
+ MEMP_STATS_DISPLAY(i);
+ }
+ SYS_STATS_DISPLAY();
+}
+#endif /* LWIP_STATS_DISPLAY */
+
+#endif /* LWIP_STATS */
+
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/sys.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/sys.c
new file mode 100644
index 0000000..7059b4d
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/sys.c
@@ -0,0 +1,106 @@
+/**
+ * @file
+ * lwIP Operating System abstraction
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+/**
+ * @defgroup sys_layer Porting (system abstraction layer)
+ * @ingroup lwip
+ * @verbinclude "sys_arch.txt"
+ *
+ * @defgroup sys_os OS abstraction layer
+ * @ingroup sys_layer
+ * No need to implement functions in this section in NO_SYS mode.
+ *
+ * @defgroup sys_sem Semaphores
+ * @ingroup sys_os
+ *
+ * @defgroup sys_mutex Mutexes
+ * @ingroup sys_os
+ * Mutexes are recommended to correctly handle priority inversion,
+ * especially if you use LWIP_CORE_LOCKING .
+ *
+ * @defgroup sys_mbox Mailboxes
+ * @ingroup sys_os
+ *
+ * @defgroup sys_time Time
+ * @ingroup sys_layer
+ *
+ * @defgroup sys_prot Critical sections
+ * @ingroup sys_layer
+ * Used to protect short regions of code against concurrent access.
+ * - Your system is a bare-metal system (probably with an RTOS)
+ * and interrupts are under your control:
+ * Implement this as LockInterrupts() / UnlockInterrupts()
+ * - Your system uses an RTOS with deferred interrupt handling from a
+ * worker thread: Implement as a global mutex or lock/unlock scheduler
+ * - Your system uses a high-level OS with e.g. POSIX signals:
+ * Implement as a global mutex
+ *
+ * @defgroup sys_misc Misc
+ * @ingroup sys_os
+ */
+
+#include "lwip/opt.h"
+
+#include "lwip/sys.h"
+
+/* Most of the functions defined in sys.h must be implemented in the
+ * architecture-dependent file sys_arch.c */
+
+#if !NO_SYS
+
+#ifndef sys_msleep
+/**
+ * Sleep for some ms. Timeouts are NOT processed while sleeping.
+ *
+ * @param ms number of milliseconds to sleep
+ */
+void
+sys_msleep(u32_t ms)
+{
+ if (ms > 0) {
+ sys_sem_t delaysem;
+ err_t err = sys_sem_new(&delaysem, 0);
+ if (err == ERR_OK) {
+ sys_arch_sem_wait(&delaysem, ms);
+ sys_sem_free(&delaysem);
+ }
+ }
+}
+#endif /* sys_msleep */
+
+#endif /* !NO_SYS */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/tcp.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/tcp.c
new file mode 100644
index 0000000..ec2e1f9
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/tcp.c
@@ -0,0 +1,2164 @@
+/**
+ * @file
+ * Transmission Control Protocol for IP
+ * See also @ref tcp_raw
+ *
+ * @defgroup tcp_raw TCP
+ * @ingroup callbackstyle_api
+ * Transmission Control Protocol for IP\n
+ * @see @ref raw_api and @ref netconn
+ *
+ * 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.\n
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_TCP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/def.h"
+#include "lwip/mem.h"
+#include "lwip/memp.h"
+#include "lwip/tcp.h"
+#include "lwip/priv/tcp_priv.h"
+#include "lwip/debug.h"
+#include "lwip/stats.h"
+#include "lwip/ip6.h"
+#include "lwip/ip6_addr.h"
+#include "lwip/nd6.h"
+
+#include <string.h>
+
+#ifdef LWIP_HOOK_FILENAME
+#include LWIP_HOOK_FILENAME
+#endif
+
+#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) ((u16_t)(((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 */
+
+/* As initial send MSS, we use TCP_MSS but limit it to 536. */
+#if TCP_MSS > 536
+#define INITIAL_MSS 536
+#else
+#define INITIAL_MSS TCP_MSS
+#endif
+
+static const char * const tcp_state_str[] = {
+ "CLOSED",
+ "LISTEN",
+ "SYN_SENT",
+ "SYN_RCVD",
+ "ESTABLISHED",
+ "FIN_WAIT_1",
+ "FIN_WAIT_2",
+ "CLOSE_WAIT",
+ "CLOSING",
+ "LAST_ACK",
+ "TIME_WAIT"
+};
+
+/* last local TCP port */
+static u16_t tcp_port = TCP_LOCAL_PORT_RANGE_START;
+
+/* Incremented every coarse grained timer shot (typically every 500 ms). */
+u32_t tcp_ticks;
+static const u8_t tcp_backoff[13] =
+ { 1, 2, 3, 4, 5, 6, 7, 7, 7, 7, 7, 7, 7};
+ /* Times per slowtmr hits */
+static const u8_t tcp_persist_backoff[7] = { 3, 6, 12, 24, 48, 96, 120 };
+
+/* The TCP PCB lists. */
+
+/** List of all TCP PCBs bound but not yet (connected || listening) */
+struct tcp_pcb *tcp_bound_pcbs;
+/** List of all TCP PCBs in LISTEN state */
+union tcp_listen_pcbs_t tcp_listen_pcbs;
+/** List of all TCP PCBs that are in a state in which
+ * they accept or send data. */
+struct tcp_pcb *tcp_active_pcbs;
+/** List of all TCP PCBs in TIME-WAIT state */
+struct tcp_pcb *tcp_tw_pcbs;
+
+/** An array with all (non-temporary) PCB lists, mainly used for smaller code size */
+struct tcp_pcb ** const tcp_pcb_lists[] = {&tcp_listen_pcbs.pcbs, &tcp_bound_pcbs,
+ &tcp_active_pcbs, &tcp_tw_pcbs};
+
+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);
+
+static err_t tcp_close_shutdown_fin(struct tcp_pcb *pcb);
+
+/**
+ * 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)
+{
+ /* Call tcp_fasttmr() every 250 ms */
+ tcp_fasttmr();
+
+ if (++tcp_timer & 1) {
+ /* Call tcp_slowtmr() every 500 ms, i.e., every other timer
+ tcp_tmr() is called. */
+ tcp_slowtmr();
+ }
+}
+
+#if LWIP_CALLBACK_API || TCP_LISTEN_BACKLOG
+/** Called when a listen pcb is closed. Iterates one pcb list and removes the
+ * closed listener pcb from pcb->listener if matching.
+ */
+static void
+tcp_remove_listener(struct tcp_pcb *list, struct tcp_pcb_listen *lpcb)
+{
+ struct tcp_pcb *pcb;
+ for (pcb = list; pcb != NULL; pcb = pcb->next) {
+ if (pcb->listener == lpcb) {
+ pcb->listener = NULL;
+ }
+ }
+}
+#endif
+
+/** Called when a listen pcb is closed. Iterates all pcb lists and removes the
+ * closed listener pcb from pcb->listener if matching.
+ */
+static void
+tcp_listen_closed(struct tcp_pcb *pcb)
+{
+#if LWIP_CALLBACK_API || TCP_LISTEN_BACKLOG
+ size_t i;
+ LWIP_ASSERT("pcb != NULL", pcb != NULL);
+ LWIP_ASSERT("pcb->state == LISTEN", pcb->state == LISTEN);
+ for (i = 1; i < LWIP_ARRAYSIZE(tcp_pcb_lists); i++) {
+ tcp_remove_listener(*tcp_pcb_lists[i], (struct tcp_pcb_listen*)pcb);
+ }
+#endif
+ LWIP_UNUSED_ARG(pcb);
+}
+
+#if TCP_LISTEN_BACKLOG
+/** @ingroup tcp_raw
+ * Delay accepting a connection in respect to the listen backlog:
+ * the number of outstanding connections is increased until
+ * tcp_backlog_accepted() is called.
+ *
+ * ATTENTION: the caller is responsible for calling tcp_backlog_accepted()
+ * or else the backlog feature will get out of sync!
+ *
+ * @param pcb the connection pcb which is not fully accepted yet
+ */
+void
+tcp_backlog_delayed(struct tcp_pcb* pcb)
+{
+ LWIP_ASSERT("pcb != NULL", pcb != NULL);
+ if ((pcb->flags & TF_BACKLOGPEND) == 0) {
+ if (pcb->listener != NULL) {
+ pcb->listener->accepts_pending++;
+ LWIP_ASSERT("accepts_pending != 0", pcb->listener->accepts_pending != 0);
+ pcb->flags |= TF_BACKLOGPEND;
+ }
+ }
+}
+
+/** @ingroup tcp_raw
+ * A delayed-accept a connection is accepted (or closed/aborted): decreases
+ * the number of outstanding connections after calling tcp_backlog_delayed().
+ *
+ * ATTENTION: the caller is responsible for calling tcp_backlog_accepted()
+ * or else the backlog feature will get out of sync!
+ *
+ * @param pcb the connection pcb which is now fully accepted (or closed/aborted)
+ */
+void
+tcp_backlog_accepted(struct tcp_pcb* pcb)
+{
+ LWIP_ASSERT("pcb != NULL", pcb != NULL);
+ if ((pcb->flags & TF_BACKLOGPEND) != 0) {
+ if (pcb->listener != NULL) {
+ LWIP_ASSERT("accepts_pending != 0", pcb->listener->accepts_pending != 0);
+ pcb->listener->accepts_pending--;
+ pcb->flags &= ~TF_BACKLOGPEND;
+ }
+ }
+}
+#endif /* TCP_LISTEN_BACKLOG */
+
+/**
+ * Closes the TX side of a connection held by the PCB.
+ * For tcp_close(), a RST is sent if the application didn't receive all data
+ * (tcp_recved() not called for all data passed to recv callback).
+ *
+ * Listening pcbs are freed and may not be referenced any more.
+ * Connection pcbs are freed if not yet connected and may not be referenced
+ * any more. If a connection is established (at least SYN received or in
+ * a closing state), the connection is closed, and put in a closing state.
+ * The pcb is then automatically freed in tcp_slowtmr(). It is therefore
+ * unsafe to reference it.
+ *
+ * @param pcb the tcp_pcb to close
+ * @return ERR_OK if connection has been closed
+ * another err_t if closing failed and pcb is not freed
+ */
+static err_t
+tcp_close_shutdown(struct tcp_pcb *pcb, u8_t rst_on_unacked_data)
+{
+ if (rst_on_unacked_data && ((pcb->state == ESTABLISHED) || (pcb->state == CLOSE_WAIT))) {
+ if ((pcb->refused_data != NULL) || (pcb->rcv_wnd != TCP_WND_MAX(pcb))) {
+ /* Not all data received by application, send RST to tell the remote
+ side about this. */
+ LWIP_ASSERT("pcb->flags & TF_RXCLOSED", pcb->flags & TF_RXCLOSED);
+
+ /* don't call tcp_abort here: we must not deallocate the pcb since
+ that might not be expected when calling tcp_close */
+ tcp_rst(pcb->snd_nxt, pcb->rcv_nxt, &pcb->local_ip, &pcb->remote_ip,
+ pcb->local_port, pcb->remote_port);
+
+ tcp_pcb_purge(pcb);
+ 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 */
+ if (tcp_input_pcb == pcb) {
+ /* prevent using a deallocated pcb: free it from tcp_input later */
+ tcp_trigger_input_pcb_close();
+ } else {
+ memp_free(MEMP_TCP_PCB, pcb);
+ }
+ }
+ return ERR_OK;
+ }
+ }
+
+ /* - states which free the pcb are handled here,
+ - states which send FIN and change state are handled in tcp_close_shutdown_fin() */
+ switch (pcb->state) {
+ case CLOSED:
+ /* Closing a pcb in the CLOSED state might seem erroneous,
+ * however, it is in this state once allocated and as yet unused
+ * and the user needs some way to free it should the need arise.
+ * Calling tcp_close() with a pcb that has already been closed, (i.e. twice)
+ * or for a pcb that has been used and then entered the CLOSED state
+ * is erroneous, but this should never happen as the pcb has in those cases
+ * been freed, and so any remaining handles are bogus. */
+ if (pcb->local_port != 0) {
+ TCP_RMV(&tcp_bound_pcbs, pcb);
+ }
+ memp_free(MEMP_TCP_PCB, pcb);
+ break;
+ case LISTEN:
+ tcp_listen_closed(pcb);
+ tcp_pcb_remove(&tcp_listen_pcbs.pcbs, pcb);
+ memp_free(MEMP_TCP_PCB_LISTEN, pcb);
+ break;
+ case SYN_SENT:
+ TCP_PCB_REMOVE_ACTIVE(pcb);
+ memp_free(MEMP_TCP_PCB, pcb);
+ MIB2_STATS_INC(mib2.tcpattemptfails);
+ break;
+ default:
+ return tcp_close_shutdown_fin(pcb);
+ }
+ return ERR_OK;
+}
+
+static err_t
+tcp_close_shutdown_fin(struct tcp_pcb *pcb)
+{
+ err_t err;
+ LWIP_ASSERT("pcb != NULL", pcb != NULL);
+
+ switch (pcb->state) {
+ case SYN_RCVD:
+ err = tcp_send_fin(pcb);
+ if (err == ERR_OK) {
+ tcp_backlog_accepted(pcb);
+ MIB2_STATS_INC(mib2.tcpattemptfails);
+ pcb->state = FIN_WAIT_1;
+ }
+ break;
+ case ESTABLISHED:
+ err = tcp_send_fin(pcb);
+ if (err == ERR_OK) {
+ MIB2_STATS_INC(mib2.tcpestabresets);
+ pcb->state = FIN_WAIT_1;
+ }
+ break;
+ case CLOSE_WAIT:
+ err = tcp_send_fin(pcb);
+ if (err == ERR_OK) {
+ MIB2_STATS_INC(mib2.tcpestabresets);
+ pcb->state = LAST_ACK;
+ }
+ break;
+ default:
+ /* Has already been closed, do nothing. */
+ return ERR_OK;
+ break;
+ }
+
+ if (err == ERR_OK) {
+ /* To ensure all data has been sent when tcp_close returns, we have
+ to make sure tcp_output doesn't fail.
+ Since we don't really have to ensure all data has been sent when tcp_close
+ returns (unsent data is sent from tcp timer functions, also), we don't care
+ for the return value of tcp_output for now. */
+ tcp_output(pcb);
+ } else if (err == ERR_MEM) {
+ /* Mark this pcb for closing. Closing is retried from tcp_tmr. */
+ pcb->flags |= TF_CLOSEPEND;
+ }
+ return err;
+}
+
+/**
+ * @ingroup tcp_raw
+ * Closes the connection held by the PCB.
+ *
+ * Listening pcbs are freed and may not be referenced any more.
+ * Connection pcbs are freed if not yet connected and may not be referenced
+ * any more. If a connection is established (at least SYN received or in
+ * a closing state), the connection is closed, and put in a closing state.
+ * The pcb is then automatically freed in tcp_slowtmr(). It is therefore
+ * unsafe to reference it (unless an error is returned).
+ *
+ * @param pcb the tcp_pcb to close
+ * @return ERR_OK if connection has been closed
+ * another err_t if closing failed and pcb is not freed
+ */
+err_t
+tcp_close(struct tcp_pcb *pcb)
+{
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_close: closing in "));
+ tcp_debug_print_state(pcb->state);
+
+ if (pcb->state != LISTEN) {
+ /* Set a flag not to receive any more data... */
+ pcb->flags |= TF_RXCLOSED;
+ }
+ /* ... and close */
+ return tcp_close_shutdown(pcb, 1);
+}
+
+/**
+ * @ingroup tcp_raw
+ * Causes all or part of a full-duplex connection of this PCB to be shut down.
+ * This doesn't deallocate the PCB unless shutting down both sides!
+ * Shutting down both sides is the same as calling tcp_close, so if it succeds,
+ * the PCB should not be referenced any more.
+ *
+ * @param pcb PCB to shutdown
+ * @param shut_rx shut down receive side if this is != 0
+ * @param shut_tx shut down send side if this is != 0
+ * @return ERR_OK if shutdown succeeded (or the PCB has already been shut down)
+ * another err_t on error.
+ */
+err_t
+tcp_shutdown(struct tcp_pcb *pcb, int shut_rx, int shut_tx)
+{
+ if (pcb->state == LISTEN) {
+ return ERR_CONN;
+ }
+ if (shut_rx) {
+ /* shut down the receive side: set a flag not to receive any more data... */
+ pcb->flags |= TF_RXCLOSED;
+ if (shut_tx) {
+ /* shutting down the tx AND rx side is the same as closing for the raw API */
+ return tcp_close_shutdown(pcb, 1);
+ }
+ /* ... and free buffered data */
+ if (pcb->refused_data != NULL) {
+ pbuf_free(pcb->refused_data);
+ pcb->refused_data = NULL;
+ }
+ }
+ if (shut_tx) {
+ /* This can't happen twice since if it succeeds, the pcb's state is changed.
+ Only close in these states as the others directly deallocate the PCB */
+ switch (pcb->state) {
+ case SYN_RCVD:
+ case ESTABLISHED:
+ case CLOSE_WAIT:
+ return tcp_close_shutdown(pcb, (u8_t)shut_rx);
+ default:
+ /* Not (yet?) connected, cannot shutdown the TX side as that would bring us
+ into CLOSED state, where the PCB is deallocated. */
+ return ERR_CONN;
+ }
+ }
+ return ERR_OK;
+}
+
+/**
+ * Abandons a connection and optionally sends a RST to the remote
+ * host. Deletes the local protocol control block. This is done when
+ * a connection is killed because of shortage of memory.
+ *
+ * @param pcb the tcp_pcb to abort
+ * @param reset boolean to indicate whether a reset should be sent
+ */
+void
+tcp_abandon(struct tcp_pcb *pcb, int reset)
+{
+ u32_t seqno, ackno;
+#if LWIP_CALLBACK_API
+ tcp_err_fn errf;
+#endif /* LWIP_CALLBACK_API */
+ void *errf_arg;
+
+ /* pcb->state LISTEN not allowed here */
+ LWIP_ASSERT("don't call tcp_abort/tcp_abandon for listen-pcbs",
+ pcb->state != LISTEN);
+ /* Figure out on which TCP PCB list we are, and remove us. If we
+ are in an active state, call the receive function associated with
+ the PCB with a NULL argument, and send an RST to the remote end. */
+ if (pcb->state == TIME_WAIT) {
+ tcp_pcb_remove(&tcp_tw_pcbs, pcb);
+ memp_free(MEMP_TCP_PCB, pcb);
+ } else {
+ int send_rst = 0;
+ u16_t local_port = 0;
+ enum tcp_state last_state;
+ seqno = pcb->snd_nxt;
+ ackno = pcb->rcv_nxt;
+#if LWIP_CALLBACK_API
+ errf = pcb->errf;
+#endif /* LWIP_CALLBACK_API */
+ errf_arg = pcb->callback_arg;
+ if (pcb->state == CLOSED) {
+ if (pcb->local_port != 0) {
+ /* bound, not yet opened */
+ TCP_RMV(&tcp_bound_pcbs, pcb);
+ }
+ } else {
+ send_rst = reset;
+ local_port = pcb->local_port;
+ TCP_PCB_REMOVE_ACTIVE(pcb);
+ }
+ if (pcb->unacked != NULL) {
+ tcp_segs_free(pcb->unacked);
+ }
+ if (pcb->unsent != NULL) {
+ tcp_segs_free(pcb->unsent);
+ }
+#if TCP_QUEUE_OOSEQ
+ if (pcb->ooseq != NULL) {
+ tcp_segs_free(pcb->ooseq);
+ }
+#endif /* TCP_QUEUE_OOSEQ */
+ tcp_backlog_accepted(pcb);
+ if (send_rst) {
+ LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_abandon: sending RST\n"));
+ tcp_rst(seqno, ackno, &pcb->local_ip, &pcb->remote_ip, local_port, pcb->remote_port);
+ }
+ last_state = pcb->state;
+ memp_free(MEMP_TCP_PCB, pcb);
+ TCP_EVENT_ERR(last_state, errf, errf_arg, ERR_ABRT);
+ }
+}
+
+/**
+ * @ingroup tcp_raw
+ * Aborts the connection by sending a RST (reset) segment to the remote
+ * host. The pcb is deallocated. This function never fails.
+ *
+ * ATTENTION: When calling this from one of the TCP callbacks, make
+ * sure you always return ERR_ABRT (and never return ERR_ABRT otherwise
+ * or you will risk accessing deallocated memory or memory leaks!
+ *
+ * @param pcb the tcp pcb to abort
+ */
+void
+tcp_abort(struct tcp_pcb *pcb)
+{
+ tcp_abandon(pcb, 1);
+}
+
+/**
+ * @ingroup tcp_raw
+ * Binds the connection to a local port number and IP address. If the
+ * IP address is not given (i.e., ipaddr == NULL), the IP address of
+ * the outgoing network interface is used instead.
+ *
+ * @param pcb the tcp_pcb to bind (no check is done whether this pcb is
+ * already bound!)
+ * @param ipaddr the local ip address to bind to (use IP4_ADDR_ANY to bind
+ * to any local address
+ * @param port the local port to bind to
+ * @return ERR_USE if the port is already in use
+ * ERR_VAL if bind failed because the PCB is not in a valid state
+ * ERR_OK if bound
+ */
+err_t
+tcp_bind(struct tcp_pcb *pcb, const ip_addr_t *ipaddr, u16_t port)
+{
+ int i;
+ int max_pcb_list = NUM_TCP_PCB_LISTS;
+ struct tcp_pcb *cpcb;
+
+#if LWIP_IPV4
+ /* Don't propagate NULL pointer (IPv4 ANY) to subsequent functions */
+ if (ipaddr == NULL) {
+ ipaddr = IP4_ADDR_ANY;
+ }
+#endif /* LWIP_IPV4 */
+
+ /* still need to check for ipaddr == NULL in IPv6 only case */
+ if ((pcb == NULL) || (ipaddr == NULL)) {
+ return ERR_VAL;
+ }
+
+ LWIP_ERROR("tcp_bind: can only bind in state CLOSED", pcb->state == CLOSED, return ERR_VAL);
+
+#if SO_REUSE
+ /* Unless the REUSEADDR flag is set,
+ we have to check the pcbs in TIME-WAIT state, also.
+ We do not dump TIME_WAIT pcb's; they can still be matched by incoming
+ packets using both local and remote IP addresses and ports to distinguish.
+ */
+ if (ip_get_option(pcb, SOF_REUSEADDR)) {
+ max_pcb_list = NUM_TCP_PCB_LISTS_NO_TIME_WAIT;
+ }
+#endif /* SO_REUSE */
+
+ if (port == 0) {
+ port = tcp_new_port();
+ if (port == 0) {
+ return ERR_BUF;
+ }
+ } else {
+ /* Check if the address already is in use (on all lists) */
+ for (i = 0; i < max_pcb_list; i++) {
+ for (cpcb = *tcp_pcb_lists[i]; cpcb != NULL; cpcb = cpcb->next) {
+ if (cpcb->local_port == port) {
+#if SO_REUSE
+ /* Omit checking for the same port if both pcbs have REUSEADDR set.
+ For SO_REUSEADDR, the duplicate-check for a 5-tuple is done in
+ tcp_connect. */
+ if (!ip_get_option(pcb, SOF_REUSEADDR) ||
+ !ip_get_option(cpcb, SOF_REUSEADDR))
+#endif /* SO_REUSE */
+ {
+ /* @todo: check accept_any_ip_version */
+ if ((IP_IS_V6(ipaddr) == IP_IS_V6_VAL(cpcb->local_ip)) &&
+ (ip_addr_isany(&cpcb->local_ip) ||
+ ip_addr_isany(ipaddr) ||
+ ip_addr_cmp(&cpcb->local_ip, ipaddr))) {
+ return ERR_USE;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (!ip_addr_isany(ipaddr)) {
+ ip_addr_set(&pcb->local_ip, ipaddr);
+ }
+ pcb->local_port = port;
+ TCP_REG(&tcp_bound_pcbs, pcb);
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_bind: bind to port %"U16_F"\n", port));
+ return ERR_OK;
+}
+#if LWIP_CALLBACK_API
+/**
+ * Default accept callback if no accept callback is specified by the user.
+ */
+static err_t
+tcp_accept_null(void *arg, struct tcp_pcb *pcb, err_t err)
+{
+ LWIP_UNUSED_ARG(arg);
+ LWIP_UNUSED_ARG(err);
+
+ tcp_abort(pcb);
+
+ return ERR_ABRT;
+}
+#endif /* LWIP_CALLBACK_API */
+
+/**
+ * @ingroup tcp_raw
+ * Set the state of the connection to be LISTEN, which means that it
+ * is able to accept incoming connections. The protocol control block
+ * is reallocated in order to consume less memory. Setting the
+ * connection to LISTEN is an irreversible process.
+ *
+ * @param pcb the original tcp_pcb
+ * @param backlog the incoming connections queue limit
+ * @return tcp_pcb used for listening, consumes less memory.
+ *
+ * @note The original tcp_pcb is freed. This function therefore has to be
+ * called like this:
+ * tpcb = tcp_listen_with_backlog(tpcb, backlog);
+ */
+struct tcp_pcb *
+tcp_listen_with_backlog(struct tcp_pcb *pcb, u8_t backlog)
+{
+ return tcp_listen_with_backlog_and_err(pcb, backlog, NULL);
+}
+
+/**
+ * @ingroup tcp_raw
+ * Set the state of the connection to be LISTEN, which means that it
+ * is able to accept incoming connections. The protocol control block
+ * is reallocated in order to consume less memory. Setting the
+ * connection to LISTEN is an irreversible process.
+ *
+ * @param pcb the original tcp_pcb
+ * @param backlog the incoming connections queue limit
+ * @param err when NULL is returned, this contains the error reason
+ * @return tcp_pcb used for listening, consumes less memory.
+ *
+ * @note The original tcp_pcb is freed. This function therefore has to be
+ * called like this:
+ * tpcb = tcp_listen_with_backlog_and_err(tpcb, backlog, &err);
+ */
+struct tcp_pcb *
+tcp_listen_with_backlog_and_err(struct tcp_pcb *pcb, u8_t backlog, err_t *err)
+{
+ struct tcp_pcb_listen *lpcb = NULL;
+ err_t res;
+
+ LWIP_UNUSED_ARG(backlog);
+ LWIP_ERROR("tcp_listen: pcb already connected", pcb->state == CLOSED, res = ERR_CLSD; goto done);
+
+ /* already listening? */
+ if (pcb->state == LISTEN) {
+ lpcb = (struct tcp_pcb_listen*)pcb;
+ res = ERR_ALREADY;
+ goto done;
+ }
+#if SO_REUSE
+ if (ip_get_option(pcb, SOF_REUSEADDR)) {
+ /* Since SOF_REUSEADDR allows reusing a local address before the pcb's usage
+ is declared (listen-/connection-pcb), we have to make sure now that
+ this port is only used once for every local IP. */
+ for (lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) {
+ if ((lpcb->local_port == pcb->local_port) &&
+ ip_addr_cmp(&lpcb->local_ip, &pcb->local_ip)) {
+ /* this address/port is already used */
+ lpcb = NULL;
+ res = ERR_USE;
+ goto done;
+ }
+ }
+ }
+#endif /* SO_REUSE */
+ lpcb = (struct tcp_pcb_listen *)memp_malloc(MEMP_TCP_PCB_LISTEN);
+ if (lpcb == NULL) {
+ res = ERR_MEM;
+ goto done;
+ }
+ lpcb->callback_arg = pcb->callback_arg;
+ lpcb->local_port = pcb->local_port;
+ lpcb->state = LISTEN;
+ lpcb->prio = pcb->prio;
+ lpcb->so_options = pcb->so_options;
+ lpcb->ttl = pcb->ttl;
+ lpcb->tos = pcb->tos;
+#if LWIP_IPV4 && LWIP_IPV6
+ IP_SET_TYPE_VAL(lpcb->remote_ip, pcb->local_ip.type);
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+ ip_addr_copy(lpcb->local_ip, pcb->local_ip);
+ if (pcb->local_port != 0) {
+ TCP_RMV(&tcp_bound_pcbs, pcb);
+ }
+ memp_free(MEMP_TCP_PCB, pcb);
+#if LWIP_CALLBACK_API
+ lpcb->accept = tcp_accept_null;
+#endif /* LWIP_CALLBACK_API */
+#if TCP_LISTEN_BACKLOG
+ lpcb->accepts_pending = 0;
+ tcp_backlog_set(lpcb, backlog);
+#endif /* TCP_LISTEN_BACKLOG */
+ TCP_REG(&tcp_listen_pcbs.pcbs, (struct tcp_pcb *)lpcb);
+ res = ERR_OK;
+done:
+ if (err != NULL) {
+ *err = res;
+ }
+ return (struct tcp_pcb *)lpcb;
+}
+
+/**
+ * Update the state that tracks the available window space to advertise.
+ *
+ * Returns how much extra window would be advertised if we sent an
+ * update now.
+ */
+u32_t
+tcp_update_rcv_ann_wnd(struct tcp_pcb *pcb)
+{
+ u32_t new_right_edge = pcb->rcv_nxt + pcb->rcv_wnd;
+
+ if (TCP_SEQ_GEQ(new_right_edge, pcb->rcv_ann_right_edge + LWIP_MIN((TCP_WND / 2), pcb->mss))) {
+ /* we can advertise more window */
+ pcb->rcv_ann_wnd = pcb->rcv_wnd;
+ return new_right_edge - pcb->rcv_ann_right_edge;
+ } else {
+ if (TCP_SEQ_GT(pcb->rcv_nxt, pcb->rcv_ann_right_edge)) {
+ /* Can happen due to other end sending out of advertised window,
+ * but within actual available (but not yet advertised) window */
+ pcb->rcv_ann_wnd = 0;
+ } else {
+ /* keep the right edge of window constant */
+ u32_t new_rcv_ann_wnd = pcb->rcv_ann_right_edge - pcb->rcv_nxt;
+#if !LWIP_WND_SCALE
+ LWIP_ASSERT("new_rcv_ann_wnd <= 0xffff", new_rcv_ann_wnd <= 0xffff);
+#endif
+ pcb->rcv_ann_wnd = (tcpwnd_size_t)new_rcv_ann_wnd;
+ }
+ return 0;
+ }
+}
+
+/**
+ * @ingroup tcp_raw
+ * This function should be called by the application when it has
+ * processed the data. The purpose is to advertise a larger window
+ * when the data has been processed.
+ *
+ * @param pcb the tcp_pcb for which data is read
+ * @param len the amount of bytes that have been read by the application
+ */
+void
+tcp_recved(struct tcp_pcb *pcb, u16_t len)
+{
+ int wnd_inflation;
+
+ /* pcb->state LISTEN not allowed here */
+ LWIP_ASSERT("don't call tcp_recved for listen-pcbs",
+ pcb->state != LISTEN);
+
+ pcb->rcv_wnd += len;
+ if (pcb->rcv_wnd > TCP_WND_MAX(pcb)) {
+ pcb->rcv_wnd = TCP_WND_MAX(pcb);
+ } else if (pcb->rcv_wnd == 0) {
+ /* rcv_wnd overflowed */
+ if ((pcb->state == CLOSE_WAIT) || (pcb->state == LAST_ACK)) {
+ /* In passive close, we allow this, since the FIN bit is added to rcv_wnd
+ by the stack itself, since it is not mandatory for an application
+ to call tcp_recved() for the FIN bit, but e.g. the netconn API does so. */
+ pcb->rcv_wnd = TCP_WND_MAX(pcb);
+ } else {
+ LWIP_ASSERT("tcp_recved: len wrapped rcv_wnd\n", 0);
+ }
+ }
+
+ wnd_inflation = tcp_update_rcv_ann_wnd(pcb);
+
+ /* If the change in the right edge of window is significant (default
+ * watermark is TCP_WND/4), then send an explicit update now.
+ * Otherwise wait for a packet to be sent in the normal course of
+ * events (or more window to be available later) */
+ if (wnd_inflation >= TCP_WND_UPDATE_THRESHOLD) {
+ tcp_ack_now(pcb);
+ tcp_output(pcb);
+ }
+
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_recved: received %"U16_F" bytes, wnd %"TCPWNDSIZE_F" (%"TCPWNDSIZE_F").\n",
+ len, pcb->rcv_wnd, (u16_t)(TCP_WND_MAX(pcb) - pcb->rcv_wnd)));
+}
+
+/**
+ * Allocate a new local TCP port.
+ *
+ * @return a new (free) local TCP port number
+ */
+static u16_t
+tcp_new_port(void)
+{
+ u8_t i;
+ u16_t n = 0;
+ struct tcp_pcb *pcb;
+
+again:
+ if (tcp_port++ == TCP_LOCAL_PORT_RANGE_END) {
+ tcp_port = TCP_LOCAL_PORT_RANGE_START;
+ }
+ /* Check all PCB lists. */
+ for (i = 0; i < NUM_TCP_PCB_LISTS; i++) {
+ for (pcb = *tcp_pcb_lists[i]; pcb != NULL; pcb = pcb->next) {
+ if (pcb->local_port == tcp_port) {
+ if (++n > (TCP_LOCAL_PORT_RANGE_END - TCP_LOCAL_PORT_RANGE_START)) {
+ return 0;
+ }
+ goto again;
+ }
+ }
+ }
+ return tcp_port;
+}
+
+/**
+ * @ingroup tcp_raw
+ * Connects to another host. The function given as the "connected"
+ * argument will be called when the connection has been established.
+ *
+ * @param pcb the tcp_pcb used to establish the connection
+ * @param ipaddr the remote ip address to connect to
+ * @param port the remote tcp port to connect to
+ * @param connected callback function to call when connected (on error,
+ the err calback will be called)
+ * @return ERR_VAL if invalid arguments are given
+ * ERR_OK if connect request has been sent
+ * other err_t values if connect request couldn't be sent
+ */
+err_t
+tcp_connect(struct tcp_pcb *pcb, const ip_addr_t *ipaddr, u16_t port,
+ tcp_connected_fn connected)
+{
+ err_t ret;
+ u32_t iss;
+ u16_t old_local_port;
+
+ if ((pcb == NULL) || (ipaddr == NULL)) {
+ return ERR_VAL;
+ }
+
+ LWIP_ERROR("tcp_connect: can only connect from state CLOSED", pcb->state == CLOSED, return ERR_ISCONN);
+
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_connect to port %"U16_F"\n", port));
+ ip_addr_set(&pcb->remote_ip, ipaddr);
+ pcb->remote_port = port;
+
+ /* check if we have a route to the remote host */
+ if (ip_addr_isany(&pcb->local_ip)) {
+ /* no local IP address set, yet. */
+ struct netif *netif;
+ const ip_addr_t *local_ip;
+ ip_route_get_local_ip(&pcb->local_ip, &pcb->remote_ip, netif, local_ip);
+ if ((netif == NULL) || (local_ip == NULL)) {
+ /* Don't even try to send a SYN packet if we have no route
+ since that will fail. */
+ return ERR_RTE;
+ }
+ /* Use the address as local address of the pcb. */
+ ip_addr_copy(pcb->local_ip, *local_ip);
+ }
+
+ old_local_port = pcb->local_port;
+ if (pcb->local_port == 0) {
+ pcb->local_port = tcp_new_port();
+ if (pcb->local_port == 0) {
+ return ERR_BUF;
+ }
+ } else {
+#if SO_REUSE
+ if (ip_get_option(pcb, SOF_REUSEADDR)) {
+ /* Since SOF_REUSEADDR allows reusing a local address, we have to make sure
+ now that the 5-tuple is unique. */
+ struct tcp_pcb *cpcb;
+ int i;
+ /* Don't check listen- and bound-PCBs, check active- and TIME-WAIT PCBs. */
+ for (i = 2; i < NUM_TCP_PCB_LISTS; i++) {
+ for (cpcb = *tcp_pcb_lists[i]; cpcb != NULL; cpcb = cpcb->next) {
+ if ((cpcb->local_port == pcb->local_port) &&
+ (cpcb->remote_port == port) &&
+ ip_addr_cmp(&cpcb->local_ip, &pcb->local_ip) &&
+ ip_addr_cmp(&cpcb->remote_ip, ipaddr)) {
+ /* linux returns EISCONN here, but ERR_USE should be OK for us */
+ return ERR_USE;
+ }
+ }
+ }
+ }
+#endif /* SO_REUSE */
+ }
+
+ iss = tcp_next_iss(pcb);
+ pcb->rcv_nxt = 0;
+ pcb->snd_nxt = iss;
+ pcb->lastack = iss - 1;
+ pcb->snd_wl2 = iss - 1;
+ pcb->snd_lbb = iss - 1;
+ /* Start with a window that does not need scaling. When window scaling is
+ enabled and used, the window is enlarged when both sides agree on scaling. */
+ pcb->rcv_wnd = pcb->rcv_ann_wnd = TCPWND_MIN16(TCP_WND);
+ pcb->rcv_ann_right_edge = pcb->rcv_nxt;
+ pcb->snd_wnd = TCP_WND;
+ /* As initial send MSS, we use TCP_MSS but limit it to 536.
+ The send MSS is updated when an MSS option is received. */
+ pcb->mss = INITIAL_MSS;
+#if TCP_CALCULATE_EFF_SEND_MSS
+ pcb->mss = tcp_eff_send_mss(pcb->mss, &pcb->local_ip, &pcb->remote_ip);
+#endif /* TCP_CALCULATE_EFF_SEND_MSS */
+ pcb->cwnd = 1;
+#if LWIP_CALLBACK_API
+ pcb->connected = connected;
+#else /* LWIP_CALLBACK_API */
+ LWIP_UNUSED_ARG(connected);
+#endif /* LWIP_CALLBACK_API */
+
+ /* Send a SYN together with the MSS option. */
+ ret = tcp_enqueue_flags(pcb, TCP_SYN);
+ if (ret == ERR_OK) {
+ /* SYN segment was enqueued, changed the pcbs state now */
+ pcb->state = SYN_SENT;
+ if (old_local_port != 0) {
+ TCP_RMV(&tcp_bound_pcbs, pcb);
+ }
+ TCP_REG_ACTIVE(pcb);
+ MIB2_STATS_INC(mib2.tcpactiveopens);
+
+ tcp_output(pcb);
+ }
+ return ret;
+}
+
+/**
+ * Called every 500 ms and implements the retransmission timer and the timer that
+ * removes PCBs that have been in TIME-WAIT for enough time. It also increments
+ * various timers such as the inactivity timer in each PCB.
+ *
+ * Automatically called from tcp_tmr().
+ */
+void
+tcp_slowtmr(void)
+{
+ struct tcp_pcb *pcb, *prev;
+ tcpwnd_size_t eff_wnd;
+ u8_t pcb_remove; /* flag if a PCB should be removed */
+ u8_t pcb_reset; /* flag if a RST should be sent when removing */
+ err_t err;
+
+ err = ERR_OK;
+
+ ++tcp_ticks;
+ ++tcp_timer_ctr;
+
+tcp_slowtmr_start:
+ /* Steps through all of the active PCBs. */
+ prev = NULL;
+ pcb = tcp_active_pcbs;
+ if (pcb == NULL) {
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: no active pcbs\n"));
+ }
+ while (pcb != NULL) {
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: processing active pcb\n"));
+ LWIP_ASSERT("tcp_slowtmr: active pcb->state != CLOSED\n", pcb->state != CLOSED);
+ LWIP_ASSERT("tcp_slowtmr: active pcb->state != LISTEN\n", pcb->state != LISTEN);
+ LWIP_ASSERT("tcp_slowtmr: active pcb->state != TIME-WAIT\n", pcb->state != TIME_WAIT);
+ if (pcb->last_timer == tcp_timer_ctr) {
+ /* skip this pcb, we have already processed it */
+ pcb = pcb->next;
+ continue;
+ }
+ pcb->last_timer = tcp_timer_ctr;
+
+ pcb_remove = 0;
+ pcb_reset = 0;
+
+ if (pcb->state == SYN_SENT && pcb->nrtx >= TCP_SYNMAXRTX) {
+ ++pcb_remove;
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: max SYN retries reached\n"));
+ }
+ else if (pcb->nrtx >= TCP_MAXRTX) {
+ ++pcb_remove;
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: max DATA retries reached\n"));
+ } else {
+ if (pcb->persist_backoff > 0) {
+ /* If snd_wnd is zero, use persist timer to send 1 byte probes
+ * instead of using the standard retransmission mechanism. */
+ u8_t backoff_cnt = tcp_persist_backoff[pcb->persist_backoff-1];
+ if (pcb->persist_cnt < backoff_cnt) {
+ pcb->persist_cnt++;
+ }
+ if (pcb->persist_cnt >= backoff_cnt) {
+ if (tcp_zero_window_probe(pcb) == ERR_OK) {
+ pcb->persist_cnt = 0;
+ if (pcb->persist_backoff < sizeof(tcp_persist_backoff)) {
+ pcb->persist_backoff++;
+ }
+ }
+ }
+ } else {
+ /* Increase the retransmission timer if it is running */
+ if (pcb->rtime >= 0) {
+ ++pcb->rtime;
+ }
+
+ if (pcb->unacked != NULL && pcb->rtime >= pcb->rto) {
+ /* Time for a retransmission. */
+ LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_slowtmr: rtime %"S16_F
+ " pcb->rto %"S16_F"\n",
+ pcb->rtime, pcb->rto));
+
+ /* Double retransmission time-out unless we are trying to
+ * connect to somebody (i.e., we are in SYN_SENT). */
+ if (pcb->state != SYN_SENT) {
+ u8_t backoff_idx = LWIP_MIN(pcb->nrtx, sizeof(tcp_backoff)-1);
+ pcb->rto = ((pcb->sa >> 3) + pcb->sv) << tcp_backoff[backoff_idx];
+ }
+
+ /* Reset the retransmission timer. */
+ pcb->rtime = 0;
+
+ /* Reduce congestion window and ssthresh. */
+ eff_wnd = LWIP_MIN(pcb->cwnd, pcb->snd_wnd);
+ pcb->ssthresh = eff_wnd >> 1;
+ if (pcb->ssthresh < (tcpwnd_size_t)(pcb->mss << 1)) {
+ pcb->ssthresh = (pcb->mss << 1);
+ }
+ pcb->cwnd = pcb->mss;
+ LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_slowtmr: cwnd %"TCPWNDSIZE_F
+ " ssthresh %"TCPWNDSIZE_F"\n",
+ pcb->cwnd, pcb->ssthresh));
+
+ /* The following needs to be called AFTER cwnd is set to one
+ mss - STJ */
+ tcp_rexmit_rto(pcb);
+ }
+ }
+ }
+ /* Check if this PCB has stayed too long in FIN-WAIT-2 */
+ if (pcb->state == FIN_WAIT_2) {
+ /* If this PCB is in FIN_WAIT_2 because of SHUT_WR don't let it time out. */
+ if (pcb->flags & TF_RXCLOSED) {
+ /* PCB was fully closed (either through close() or SHUT_RDWR):
+ normal FIN-WAIT timeout handling. */
+ if ((u32_t)(tcp_ticks - pcb->tmr) >
+ TCP_FIN_WAIT_TIMEOUT / TCP_SLOW_INTERVAL) {
+ ++pcb_remove;
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: removing pcb stuck in FIN-WAIT-2\n"));
+ }
+ }
+ }
+
+ /* Check if KEEPALIVE should be sent */
+ if (ip_get_option(pcb, SOF_KEEPALIVE) &&
+ ((pcb->state == ESTABLISHED) ||
+ (pcb->state == CLOSE_WAIT))) {
+ if ((u32_t)(tcp_ticks - pcb->tmr) >
+ (pcb->keep_idle + TCP_KEEP_DUR(pcb)) / TCP_SLOW_INTERVAL)
+ {
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: KEEPALIVE timeout. Aborting connection to "));
+ ip_addr_debug_print(TCP_DEBUG, &pcb->remote_ip);
+ LWIP_DEBUGF(TCP_DEBUG, ("\n"));
+
+ ++pcb_remove;
+ ++pcb_reset;
+ } else if ((u32_t)(tcp_ticks - pcb->tmr) >
+ (pcb->keep_idle + pcb->keep_cnt_sent * TCP_KEEP_INTVL(pcb))
+ / TCP_SLOW_INTERVAL)
+ {
+ err = tcp_keepalive(pcb);
+ if (err == ERR_OK) {
+ pcb->keep_cnt_sent++;
+ }
+ }
+ }
+
+ /* If this PCB has queued out of sequence data, but has been
+ inactive for too long, will drop the data (it will eventually
+ be retransmitted). */
+#if TCP_QUEUE_OOSEQ
+ if (pcb->ooseq != NULL &&
+ (u32_t)tcp_ticks - pcb->tmr >= pcb->rto * TCP_OOSEQ_TIMEOUT) {
+ tcp_segs_free(pcb->ooseq);
+ pcb->ooseq = NULL;
+ LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_slowtmr: dropping OOSEQ queued data\n"));
+ }
+#endif /* TCP_QUEUE_OOSEQ */
+
+ /* Check if this PCB has stayed too long in SYN-RCVD */
+ if (pcb->state == SYN_RCVD) {
+ if ((u32_t)(tcp_ticks - pcb->tmr) >
+ TCP_SYN_RCVD_TIMEOUT / TCP_SLOW_INTERVAL) {
+ ++pcb_remove;
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: removing pcb stuck in SYN-RCVD\n"));
+ }
+ }
+
+ /* Check if this PCB has stayed too long in LAST-ACK */
+ if (pcb->state == LAST_ACK) {
+ if ((u32_t)(tcp_ticks - pcb->tmr) > 2 * TCP_MSL / TCP_SLOW_INTERVAL) {
+ ++pcb_remove;
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: removing pcb stuck in LAST-ACK\n"));
+ }
+ }
+
+ /* If the PCB should be removed, do it. */
+ if (pcb_remove) {
+ struct tcp_pcb *pcb2;
+#if LWIP_CALLBACK_API
+ tcp_err_fn err_fn = pcb->errf;
+#endif /* LWIP_CALLBACK_API */
+ void *err_arg;
+ enum tcp_state last_state;
+ tcp_pcb_purge(pcb);
+ /* Remove PCB from tcp_active_pcbs list. */
+ if (prev != NULL) {
+ LWIP_ASSERT("tcp_slowtmr: middle tcp != tcp_active_pcbs", pcb != tcp_active_pcbs);
+ prev->next = pcb->next;
+ } else {
+ /* This PCB was the first. */
+ LWIP_ASSERT("tcp_slowtmr: first pcb == tcp_active_pcbs", tcp_active_pcbs == pcb);
+ tcp_active_pcbs = pcb->next;
+ }
+
+ if (pcb_reset) {
+ tcp_rst(pcb->snd_nxt, pcb->rcv_nxt, &pcb->local_ip, &pcb->remote_ip,
+ pcb->local_port, pcb->remote_port);
+ }
+
+ err_arg = pcb->callback_arg;
+ last_state = pcb->state;
+ pcb2 = pcb;
+ pcb = pcb->next;
+ memp_free(MEMP_TCP_PCB, pcb2);
+
+ tcp_active_pcbs_changed = 0;
+ TCP_EVENT_ERR(last_state, err_fn, err_arg, ERR_ABRT);
+ if (tcp_active_pcbs_changed) {
+ goto tcp_slowtmr_start;
+ }
+ } else {
+ /* get the 'next' element now and work with 'prev' below (in case of abort) */
+ prev = pcb;
+ pcb = pcb->next;
+
+ /* We check if we should poll the connection. */
+ ++prev->polltmr;
+ if (prev->polltmr >= prev->pollinterval) {
+ prev->polltmr = 0;
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: polling application\n"));
+ tcp_active_pcbs_changed = 0;
+ TCP_EVENT_POLL(prev, err);
+ if (tcp_active_pcbs_changed) {
+ goto tcp_slowtmr_start;
+ }
+ /* if err == ERR_ABRT, 'prev' is already deallocated */
+ if (err == ERR_OK) {
+ tcp_output(prev);
+ }
+ }
+ }
+ }
+
+
+ /* Steps through all of the TIME-WAIT PCBs. */
+ prev = NULL;
+ pcb = tcp_tw_pcbs;
+ while (pcb != NULL) {
+ LWIP_ASSERT("tcp_slowtmr: TIME-WAIT pcb->state == TIME-WAIT", pcb->state == TIME_WAIT);
+ pcb_remove = 0;
+
+ /* Check if this PCB has stayed long enough in TIME-WAIT */
+ if ((u32_t)(tcp_ticks - pcb->tmr) > 2 * TCP_MSL / TCP_SLOW_INTERVAL) {
+ ++pcb_remove;
+ }
+
+ /* If the PCB should be removed, do it. */
+ if (pcb_remove) {
+ struct tcp_pcb *pcb2;
+ tcp_pcb_purge(pcb);
+ /* Remove PCB from tcp_tw_pcbs list. */
+ if (prev != NULL) {
+ LWIP_ASSERT("tcp_slowtmr: middle tcp != tcp_tw_pcbs", pcb != tcp_tw_pcbs);
+ prev->next = pcb->next;
+ } else {
+ /* This PCB was the first. */
+ LWIP_ASSERT("tcp_slowtmr: first pcb == tcp_tw_pcbs", tcp_tw_pcbs == pcb);
+ tcp_tw_pcbs = pcb->next;
+ }
+ pcb2 = pcb;
+ pcb = pcb->next;
+ memp_free(MEMP_TCP_PCB, pcb2);
+ } else {
+ prev = pcb;
+ pcb = pcb->next;
+ }
+ }
+}
+
+/**
+ * Is called every TCP_FAST_INTERVAL (250 ms) and process data previously
+ * "refused" by upper layer (application) and sends delayed ACKs.
+ *
+ * Automatically called from tcp_tmr().
+ */
+void
+tcp_fasttmr(void)
+{
+ struct tcp_pcb *pcb;
+
+ ++tcp_timer_ctr;
+
+tcp_fasttmr_start:
+ pcb = tcp_active_pcbs;
+
+ while (pcb != NULL) {
+ if (pcb->last_timer != tcp_timer_ctr) {
+ struct tcp_pcb *next;
+ pcb->last_timer = tcp_timer_ctr;
+ /* send delayed ACKs */
+ if (pcb->flags & TF_ACK_DELAY) {
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_fasttmr: delayed ACK\n"));
+ tcp_ack_now(pcb);
+ tcp_output(pcb);
+ pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW);
+ }
+ /* send pending FIN */
+ if (pcb->flags & TF_CLOSEPEND) {
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_fasttmr: pending FIN\n"));
+ pcb->flags &= ~(TF_CLOSEPEND);
+ tcp_close_shutdown_fin(pcb);
+ }
+
+ 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;
+ } else {
+ pcb = pcb->next;
+ }
+ }
+}
+
+/** Call tcp_output for all active pcbs that have TF_NAGLEMEMERR set */
+void
+tcp_txnow(void)
+{
+ struct tcp_pcb *pcb;
+
+ for (pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) {
+ if (pcb->flags & TF_NAGLEMEMERR) {
+ tcp_output(pcb);
+ }
+ }
+}
+
+/** Pass pcb->refused_data to the recv callback */
+err_t
+tcp_process_refused_data(struct tcp_pcb *pcb)
+{
+#if TCP_QUEUE_OOSEQ && LWIP_WND_SCALE
+ struct pbuf *rest;
+ while (pcb->refused_data != NULL)
+#endif /* TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */
+ {
+ 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;
+#if TCP_QUEUE_OOSEQ && LWIP_WND_SCALE
+ pbuf_split_64k(refused_data, &rest);
+ pcb->refused_data = rest;
+#else /* TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */
+ pcb->refused_data = NULL;
+#endif /* TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */
+ /* 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
+#if TCP_QUEUE_OOSEQ && LWIP_WND_SCALE
+ && (rest == NULL)
+#endif /* TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */
+ ) {
+ /* correct rcv_wnd as the application won't call tcp_recved()
+ for the FIN's seqno */
+ if (pcb->rcv_wnd != TCP_WND_MAX(pcb)) {
+ 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) */
+#if TCP_QUEUE_OOSEQ && LWIP_WND_SCALE
+ if (rest != NULL) {
+ pbuf_cat(refused_data, rest);
+ }
+#endif /* TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */
+ pcb->refused_data = refused_data;
+ return ERR_INPROGRESS;
+ }
+ }
+ return ERR_OK;
+}
+
+/**
+ * Deallocates a list of TCP segments (tcp_seg structures).
+ *
+ * @param seg tcp_seg list of TCP segments to free
+ */
+void
+tcp_segs_free(struct tcp_seg *seg)
+{
+ while (seg != NULL) {
+ struct tcp_seg *next = seg->next;
+ tcp_seg_free(seg);
+ seg = next;
+ }
+}
+
+/**
+ * Frees a TCP segment (tcp_seg structure).
+ *
+ * @param seg single tcp_seg to free
+ */
+void
+tcp_seg_free(struct tcp_seg *seg)
+{
+ if (seg != NULL) {
+ if (seg->p != NULL) {
+ pbuf_free(seg->p);
+#if TCP_DEBUG
+ seg->p = NULL;
+#endif /* TCP_DEBUG */
+ }
+ memp_free(MEMP_TCP_SEG, seg);
+ }
+}
+
+/**
+ * Sets the priority of a connection.
+ *
+ * @param pcb the tcp_pcb to manipulate
+ * @param prio new priority
+ */
+void
+tcp_setprio(struct tcp_pcb *pcb, u8_t prio)
+{
+ pcb->prio = prio;
+}
+
+#if TCP_QUEUE_OOSEQ
+/**
+ * Returns a copy of the given TCP segment.
+ * The pbuf and data are not copied, only the pointers
+ *
+ * @param seg the old tcp_seg
+ * @return a copy of seg
+ */
+struct tcp_seg *
+tcp_seg_copy(struct tcp_seg *seg)
+{
+ struct tcp_seg *cseg;
+
+ cseg = (struct tcp_seg *)memp_malloc(MEMP_TCP_SEG);
+ if (cseg == NULL) {
+ return NULL;
+ }
+ SMEMCPY((u8_t *)cseg, (const u8_t *)seg, sizeof(struct tcp_seg));
+ pbuf_ref(cseg->p);
+ return cseg;
+}
+#endif /* TCP_QUEUE_OOSEQ */
+
+#if LWIP_CALLBACK_API
+/**
+ * Default receive callback that is called if the user didn't register
+ * a recv callback for the pcb.
+ */
+err_t
+tcp_recv_null(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
+{
+ LWIP_UNUSED_ARG(arg);
+ if (p != NULL) {
+ tcp_recved(pcb, p->tot_len);
+ pbuf_free(p);
+ } else if (err == ERR_OK) {
+ return tcp_close(pcb);
+ }
+ return ERR_OK;
+}
+#endif /* LWIP_CALLBACK_API */
+
+/**
+ * Kills the oldest active connection that has the same or lower priority than
+ * 'prio'.
+ *
+ * @param prio minimum priority
+ */
+static void
+tcp_kill_prio(u8_t prio)
+{
+ struct tcp_pcb *pcb, *inactive;
+ u32_t inactivity;
+ u8_t mprio;
+
+ mprio = LWIP_MIN(TCP_PRIO_MAX, prio);
+
+ /* We kill the oldest active connection that has lower priority than prio. */
+ inactivity = 0;
+ inactive = NULL;
+ for (pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) {
+ if (pcb->prio <= mprio &&
+ (u32_t)(tcp_ticks - pcb->tmr) >= inactivity) {
+ inactivity = tcp_ticks - pcb->tmr;
+ inactive = pcb;
+ mprio = pcb->prio;
+ }
+ }
+ if (inactive != NULL) {
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_kill_prio: killing oldest PCB %p (%"S32_F")\n",
+ (void *)inactive, inactivity));
+ tcp_abort(inactive);
+ }
+}
+
+/**
+ * Kills the oldest connection that is in specific state.
+ * Called from tcp_alloc() for LAST_ACK and CLOSING if no more connections are available.
+ */
+static void
+tcp_kill_state(enum tcp_state state)
+{
+ struct tcp_pcb *pcb, *inactive;
+ u32_t inactivity;
+
+ LWIP_ASSERT("invalid state", (state == CLOSING) || (state == LAST_ACK));
+
+ inactivity = 0;
+ inactive = NULL;
+ /* Go through the list of active pcbs and get the oldest pcb that is in state
+ CLOSING/LAST_ACK. */
+ for (pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) {
+ if (pcb->state == state) {
+ if ((u32_t)(tcp_ticks - pcb->tmr) >= inactivity) {
+ inactivity = tcp_ticks - pcb->tmr;
+ inactive = pcb;
+ }
+ }
+ }
+ if (inactive != NULL) {
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_kill_closing: killing oldest %s PCB %p (%"S32_F")\n",
+ tcp_state_str[state], (void *)inactive, inactivity));
+ /* Don't send a RST, since no data is lost. */
+ tcp_abandon(inactive, 0);
+ }
+}
+
+/**
+ * Kills the oldest connection that is in TIME_WAIT state.
+ * Called from tcp_alloc() if no more connections are available.
+ */
+static void
+tcp_kill_timewait(void)
+{
+ struct tcp_pcb *pcb, *inactive;
+ u32_t inactivity;
+
+ inactivity = 0;
+ inactive = NULL;
+ /* Go through the list of TIME_WAIT pcbs and get the oldest pcb. */
+ for (pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) {
+ if ((u32_t)(tcp_ticks - pcb->tmr) >= inactivity) {
+ inactivity = tcp_ticks - pcb->tmr;
+ inactive = pcb;
+ }
+ }
+ if (inactive != NULL) {
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_kill_timewait: killing oldest TIME-WAIT PCB %p (%"S32_F")\n",
+ (void *)inactive, inactivity));
+ tcp_abort(inactive);
+ }
+}
+
+/**
+ * Allocate a new tcp_pcb structure.
+ *
+ * @param prio priority for the new pcb
+ * @return a new tcp_pcb that initially is in state CLOSED
+ */
+struct tcp_pcb *
+tcp_alloc(u8_t prio)
+{
+ struct tcp_pcb *pcb;
+
+ pcb = (struct tcp_pcb *)memp_malloc(MEMP_TCP_PCB);
+ if (pcb == NULL) {
+ /* Try killing oldest connection in TIME-WAIT. */
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_alloc: killing off oldest TIME-WAIT connection\n"));
+ tcp_kill_timewait();
+ /* Try to allocate a tcp_pcb again. */
+ pcb = (struct tcp_pcb *)memp_malloc(MEMP_TCP_PCB);
+ if (pcb == NULL) {
+ /* Try killing oldest connection in LAST-ACK (these wouldn't go to TIME-WAIT). */
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_alloc: killing off oldest LAST-ACK connection\n"));
+ tcp_kill_state(LAST_ACK);
+ /* Try to allocate a tcp_pcb again. */
+ pcb = (struct tcp_pcb *)memp_malloc(MEMP_TCP_PCB);
+ if (pcb == NULL) {
+ /* Try killing oldest connection in CLOSING. */
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_alloc: killing off oldest CLOSING connection\n"));
+ tcp_kill_state(CLOSING);
+ /* Try to allocate a tcp_pcb again. */
+ pcb = (struct tcp_pcb *)memp_malloc(MEMP_TCP_PCB);
+ if (pcb == NULL) {
+ /* Try killing active connections with lower priority than the new one. */
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_alloc: killing connection with prio lower than %d\n", prio));
+ tcp_kill_prio(prio);
+ /* Try to allocate a tcp_pcb again. */
+ pcb = (struct tcp_pcb *)memp_malloc(MEMP_TCP_PCB);
+ if (pcb != NULL) {
+ /* adjust err stats: memp_malloc failed multiple times before */
+ MEMP_STATS_DEC(err, MEMP_TCP_PCB);
+ }
+ }
+ if (pcb != NULL) {
+ /* adjust err stats: memp_malloc failed multiple times before */
+ MEMP_STATS_DEC(err, MEMP_TCP_PCB);
+ }
+ }
+ if (pcb != NULL) {
+ /* adjust err stats: memp_malloc failed multiple times before */
+ MEMP_STATS_DEC(err, MEMP_TCP_PCB);
+ }
+ }
+ if (pcb != NULL) {
+ /* adjust err stats: memp_malloc failed above */
+ MEMP_STATS_DEC(err, MEMP_TCP_PCB);
+ }
+ }
+ if (pcb != NULL) {
+ /* zero out the whole pcb, so there is no need to initialize members to zero */
+ memset(pcb, 0, sizeof(struct tcp_pcb));
+ pcb->prio = prio;
+ pcb->snd_buf = TCP_SND_BUF;
+ /* Start with a window that does not need scaling. When window scaling is
+ enabled and used, the window is enlarged when both sides agree on scaling. */
+ pcb->rcv_wnd = pcb->rcv_ann_wnd = TCPWND_MIN16(TCP_WND);
+ pcb->ttl = TCP_TTL;
+ /* As initial send MSS, we use TCP_MSS but limit it to 536.
+ The send MSS is updated when an MSS option is received. */
+ pcb->mss = INITIAL_MSS;
+ pcb->rto = 3000 / TCP_SLOW_INTERVAL;
+ pcb->sv = 3000 / TCP_SLOW_INTERVAL;
+ pcb->rtime = -1;
+ pcb->cwnd = 1;
+ pcb->tmr = tcp_ticks;
+ pcb->last_timer = tcp_timer_ctr;
+
+ /* RFC 5681 recommends setting ssthresh abritrarily high and gives an example
+ of using the largest advertised receive window. We've seen complications with
+ receiving TCPs that use window scaling and/or window auto-tuning where the
+ initial advertised window is very small and then grows rapidly once the
+ connection is established. To avoid these complications, we set ssthresh to the
+ largest effective cwnd (amount of in-flight data) that the sender can have. */
+ pcb->ssthresh = TCP_SND_BUF;
+
+#if LWIP_CALLBACK_API
+ pcb->recv = tcp_recv_null;
+#endif /* LWIP_CALLBACK_API */
+
+ /* Init KEEPALIVE timer */
+ pcb->keep_idle = TCP_KEEPIDLE_DEFAULT;
+
+#if LWIP_TCP_KEEPALIVE
+ pcb->keep_intvl = TCP_KEEPINTVL_DEFAULT;
+ pcb->keep_cnt = TCP_KEEPCNT_DEFAULT;
+#endif /* LWIP_TCP_KEEPALIVE */
+ }
+ return pcb;
+}
+
+/**
+ * @ingroup tcp_raw
+ * Creates a new TCP protocol control block but doesn't place it on
+ * any of the TCP PCB lists.
+ * The pcb is not put on any list until binding using tcp_bind().
+ *
+ * @internal: Maybe there should be a idle TCP PCB list where these
+ * PCBs are put on. Port reservation using tcp_bind() is implemented but
+ * allocated pcbs that are not bound can't be killed automatically if wanting
+ * to allocate a pcb with higher prio (@see tcp_kill_prio())
+ *
+ * @return a new tcp_pcb that initially is in state CLOSED
+ */
+struct tcp_pcb *
+tcp_new(void)
+{
+ return tcp_alloc(TCP_PRIO_NORMAL);
+}
+
+/**
+ * @ingroup tcp_raw
+ * Creates a new TCP protocol control block but doesn't
+ * place it on any of the TCP PCB lists.
+ * The pcb is not put on any list until binding using tcp_bind().
+ *
+ * @param type IP address type, see @ref lwip_ip_addr_type definitions.
+ * If you want to listen to IPv4 and IPv6 (dual-stack) connections,
+ * supply @ref IPADDR_TYPE_ANY as argument and bind to @ref IP_ANY_TYPE.
+ * @return a new tcp_pcb that initially is in state CLOSED
+ */
+struct tcp_pcb *
+tcp_new_ip_type(u8_t type)
+{
+ struct tcp_pcb * pcb;
+ pcb = tcp_alloc(TCP_PRIO_NORMAL);
+#if LWIP_IPV4 && LWIP_IPV6
+ if (pcb != NULL) {
+ IP_SET_TYPE_VAL(pcb->local_ip, type);
+ IP_SET_TYPE_VAL(pcb->remote_ip, type);
+ }
+#else
+ LWIP_UNUSED_ARG(type);
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+ return pcb;
+}
+
+/**
+ * @ingroup tcp_raw
+ * Used to specify the argument that should be passed callback
+ * functions.
+ *
+ * @param pcb tcp_pcb to set the callback argument
+ * @param arg void pointer argument to pass to callback functions
+ */
+void
+tcp_arg(struct tcp_pcb *pcb, void *arg)
+{
+ /* This function is allowed to be called for both listen pcbs and
+ connection pcbs. */
+ if (pcb != NULL) {
+ pcb->callback_arg = arg;
+ }
+}
+#if LWIP_CALLBACK_API
+
+/**
+ * @ingroup tcp_raw
+ * Used to specify the function that should be called when a TCP
+ * connection receives data.
+ *
+ * @param pcb tcp_pcb to set the recv callback
+ * @param recv callback function to call for this pcb when data is received
+ */
+void
+tcp_recv(struct tcp_pcb *pcb, tcp_recv_fn recv)
+{
+ if (pcb != NULL) {
+ LWIP_ASSERT("invalid socket state for recv callback", pcb->state != LISTEN);
+ pcb->recv = recv;
+ }
+}
+
+/**
+ * @ingroup tcp_raw
+ * Used to specify the function that should be called when TCP data
+ * has been successfully delivered to the remote host.
+ *
+ * @param pcb tcp_pcb to set the sent callback
+ * @param sent callback function to call for this pcb when data is successfully sent
+ */
+void
+tcp_sent(struct tcp_pcb *pcb, tcp_sent_fn sent)
+{
+ if (pcb != NULL) {
+ LWIP_ASSERT("invalid socket state for sent callback", pcb->state != LISTEN);
+ pcb->sent = sent;
+ }
+}
+
+/**
+ * @ingroup tcp_raw
+ * Used to specify the function that should be called when a fatal error
+ * has occurred on the connection.
+ *
+ * @note The corresponding pcb is already freed when this callback is called!
+ *
+ * @param pcb tcp_pcb to set the err callback
+ * @param err callback function to call for this pcb when a fatal error
+ * has occurred on the connection
+ */
+void
+tcp_err(struct tcp_pcb *pcb, tcp_err_fn err)
+{
+ if (pcb != NULL) {
+ LWIP_ASSERT("invalid socket state for err callback", pcb->state != LISTEN);
+ pcb->errf = err;
+ }
+}
+
+/**
+ * @ingroup tcp_raw
+ * Used for specifying the function that should be called when a
+ * LISTENing connection has been connected to another host.
+ *
+ * @param pcb tcp_pcb to set the accept callback
+ * @param accept callback function to call for this pcb when LISTENing
+ * connection has been connected to another host
+ */
+void
+tcp_accept(struct tcp_pcb *pcb, tcp_accept_fn accept)
+{
+ if ((pcb != NULL) && (pcb->state == LISTEN)) {
+ struct tcp_pcb_listen *lpcb = (struct tcp_pcb_listen*)pcb;
+ lpcb->accept = accept;
+ }
+}
+#endif /* LWIP_CALLBACK_API */
+
+
+/**
+ * @ingroup tcp_raw
+ * Used to specify the function that should be called periodically
+ * from TCP. The interval is specified in terms of the TCP coarse
+ * timer interval, which is called twice a second.
+ *
+ */
+void
+tcp_poll(struct tcp_pcb *pcb, tcp_poll_fn poll, u8_t interval)
+{
+ LWIP_ASSERT("invalid socket state for poll", pcb->state != LISTEN);
+#if LWIP_CALLBACK_API
+ pcb->poll = poll;
+#else /* LWIP_CALLBACK_API */
+ LWIP_UNUSED_ARG(poll);
+#endif /* LWIP_CALLBACK_API */
+ pcb->pollinterval = interval;
+}
+
+/**
+ * Purges a TCP PCB. Removes any buffered data and frees the buffer memory
+ * (pcb->ooseq, pcb->unsent and pcb->unacked are freed).
+ *
+ * @param pcb tcp_pcb to purge. The pcb itself is not deallocated!
+ */
+void
+tcp_pcb_purge(struct tcp_pcb *pcb)
+{
+ if (pcb->state != CLOSED &&
+ pcb->state != TIME_WAIT &&
+ pcb->state != LISTEN) {
+
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge\n"));
+
+ tcp_backlog_accepted(pcb);
+
+ if (pcb->refused_data != NULL) {
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: data left on ->refused_data\n"));
+ pbuf_free(pcb->refused_data);
+ pcb->refused_data = NULL;
+ }
+ if (pcb->unsent != NULL) {
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: not all data sent\n"));
+ }
+ if (pcb->unacked != NULL) {
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: data left on ->unacked\n"));
+ }
+#if TCP_QUEUE_OOSEQ
+ if (pcb->ooseq != NULL) {
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: data left on ->ooseq\n"));
+ }
+ tcp_segs_free(pcb->ooseq);
+ pcb->ooseq = NULL;
+#endif /* TCP_QUEUE_OOSEQ */
+
+ /* Stop the retransmission timer as it will expect data on unacked
+ queue if it fires */
+ pcb->rtime = -1;
+
+ tcp_segs_free(pcb->unsent);
+ tcp_segs_free(pcb->unacked);
+ pcb->unacked = pcb->unsent = NULL;
+#if TCP_OVERSIZE
+ pcb->unsent_oversize = 0;
+#endif /* TCP_OVERSIZE */
+ }
+}
+
+/**
+ * Purges the PCB and removes it from a PCB list. Any delayed ACKs are sent first.
+ *
+ * @param pcblist PCB list to purge.
+ * @param pcb tcp_pcb to purge. The pcb itself is NOT deallocated!
+ */
+void
+tcp_pcb_remove(struct tcp_pcb **pcblist, struct tcp_pcb *pcb)
+{
+ TCP_RMV(pcblist, pcb);
+
+ tcp_pcb_purge(pcb);
+
+ /* if there is an outstanding delayed ACKs, send it */
+ if (pcb->state != TIME_WAIT &&
+ pcb->state != LISTEN &&
+ pcb->flags & TF_ACK_DELAY) {
+ pcb->flags |= TF_ACK_NOW;
+ tcp_output(pcb);
+ }
+
+ if (pcb->state != LISTEN) {
+ LWIP_ASSERT("unsent segments leaking", pcb->unsent == NULL);
+ LWIP_ASSERT("unacked segments leaking", pcb->unacked == NULL);
+#if TCP_QUEUE_OOSEQ
+ LWIP_ASSERT("ooseq segments leaking", pcb->ooseq == NULL);
+#endif /* TCP_QUEUE_OOSEQ */
+ }
+
+ pcb->state = CLOSED;
+ /* reset the local port to prevent the pcb from being 'bound' */
+ pcb->local_port = 0;
+
+ LWIP_ASSERT("tcp_pcb_remove: tcp_pcbs_sane()", tcp_pcbs_sane());
+}
+
+/**
+ * Calculates a new initial sequence number for new connections.
+ *
+ * @return u32_t pseudo random sequence number
+ */
+u32_t
+tcp_next_iss(struct tcp_pcb *pcb)
+{
+#ifdef LWIP_HOOK_TCP_ISN
+ return LWIP_HOOK_TCP_ISN(&pcb->local_ip, pcb->local_port, &pcb->remote_ip, pcb->remote_port);
+#else /* LWIP_HOOK_TCP_ISN */
+ static u32_t iss = 6510;
+
+ LWIP_UNUSED_ARG(pcb);
+
+ iss += tcp_ticks; /* XXX */
+ return iss;
+#endif /* LWIP_HOOK_TCP_ISN */
+}
+
+#if TCP_CALCULATE_EFF_SEND_MSS
+/**
+ * Calculates the effective send mss that can be used for a specific IP address
+ * by using ip_route to determine the netif used to send to the address and
+ * calculating the minimum of TCP_MSS and that netif's mtu (if set).
+ */
+u16_t
+tcp_eff_send_mss_impl(u16_t sendmss, const ip_addr_t *dest
+#if LWIP_IPV6 || LWIP_IPV4_SRC_ROUTING
+ , const ip_addr_t *src
+#endif /* LWIP_IPV6 || LWIP_IPV4_SRC_ROUTING */
+ )
+{
+ u16_t mss_s;
+ struct netif *outif;
+ s16_t mtu;
+
+ outif = ip_route(src, dest);
+#if LWIP_IPV6
+#if LWIP_IPV4
+ if (IP_IS_V6(dest))
+#endif /* LWIP_IPV4 */
+ {
+ /* First look in destination cache, to see if there is a Path MTU. */
+ mtu = nd6_get_destination_mtu(ip_2_ip6(dest), outif);
+ }
+#if LWIP_IPV4
+ else
+#endif /* LWIP_IPV4 */
+#endif /* LWIP_IPV6 */
+#if LWIP_IPV4
+ {
+ if (outif == NULL) {
+ return sendmss;
+ }
+ mtu = outif->mtu;
+ }
+#endif /* LWIP_IPV4 */
+
+ if (mtu != 0) {
+#if LWIP_IPV6
+#if LWIP_IPV4
+ if (IP_IS_V6(dest))
+#endif /* LWIP_IPV4 */
+ {
+ mss_s = mtu - IP6_HLEN - TCP_HLEN;
+ }
+#if LWIP_IPV4
+ else
+#endif /* LWIP_IPV4 */
+#endif /* LWIP_IPV6 */
+#if LWIP_IPV4
+ {
+ mss_s = mtu - IP_HLEN - TCP_HLEN;
+ }
+#endif /* LWIP_IPV4 */
+ /* RFC 1122, chap 4.2.2.6:
+ * Eff.snd.MSS = min(SendMSS+20, MMS_S) - TCPhdrsize - IPoptionsize
+ * We correct for TCP options in tcp_write(), and don't support IP options.
+ */
+ sendmss = LWIP_MIN(sendmss, mss_s);
+ }
+ return sendmss;
+}
+#endif /* TCP_CALCULATE_EFF_SEND_MSS */
+
+/** Helper function for tcp_netif_ip_addr_changed() that iterates a pcb list */
+static void
+tcp_netif_ip_addr_changed_pcblist(const ip_addr_t* old_addr, struct tcp_pcb* pcb_list)
+{
+ struct tcp_pcb *pcb;
+ pcb = pcb_list;
+ while (pcb != NULL) {
+ /* PCB bound to current local interface address? */
+ if (ip_addr_cmp(&pcb->local_ip, old_addr)
+#if LWIP_AUTOIP
+ /* connections to link-local addresses must persist (RFC3927 ch. 1.9) */
+ && (!IP_IS_V4_VAL(pcb->local_ip) || !ip4_addr_islinklocal(ip_2_ip4(&pcb->local_ip)))
+#endif /* LWIP_AUTOIP */
+ ) {
+ /* this connection must be aborted */
+ struct tcp_pcb *next = pcb->next;
+ LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_STATE, ("netif_set_ipaddr: aborting TCP pcb %p\n", (void *)pcb));
+ tcp_abort(pcb);
+ pcb = next;
+ } else {
+ pcb = pcb->next;
+ }
+ }
+}
+
+/** This function is called from netif.c when address is changed or netif is removed
+ *
+ * @param old_addr IP address of the netif before change
+ * @param new_addr IP address of the netif after change or NULL if netif has been removed
+ */
+void
+tcp_netif_ip_addr_changed(const ip_addr_t* old_addr, const ip_addr_t* new_addr)
+{
+ struct tcp_pcb_listen *lpcb, *next;
+
+ if (!ip_addr_isany(old_addr)) {
+ tcp_netif_ip_addr_changed_pcblist(old_addr, tcp_active_pcbs);
+ tcp_netif_ip_addr_changed_pcblist(old_addr, tcp_bound_pcbs);
+
+ if (!ip_addr_isany(new_addr)) {
+ /* PCB bound to current local interface address? */
+ for (lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = next) {
+ next = lpcb->next;
+ /* PCB bound to current local interface address? */
+ if (ip_addr_cmp(&lpcb->local_ip, old_addr)) {
+ /* The PCB is listening to the old ipaddr and
+ * is set to listen to the new one instead */
+ ip_addr_copy(lpcb->local_ip, *new_addr);
+ }
+ }
+ }
+ }
+}
+
+const char*
+tcp_debug_state_str(enum tcp_state s)
+{
+ return tcp_state_str[s];
+}
+
+#if TCP_DEBUG || TCP_INPUT_DEBUG || TCP_OUTPUT_DEBUG
+/**
+ * Print a tcp header for debugging purposes.
+ *
+ * @param tcphdr pointer to a struct tcp_hdr
+ */
+void
+tcp_debug_print(struct tcp_hdr *tcphdr)
+{
+ LWIP_DEBUGF(TCP_DEBUG, ("TCP header:\n"));
+ LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n"));
+ LWIP_DEBUGF(TCP_DEBUG, ("| %5"U16_F" | %5"U16_F" | (src port, dest port)\n",
+ lwip_ntohs(tcphdr->src), lwip_ntohs(tcphdr->dest)));
+ LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n"));
+ LWIP_DEBUGF(TCP_DEBUG, ("| %010"U32_F" | (seq no)\n",
+ lwip_ntohl(tcphdr->seqno)));
+ LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n"));
+ LWIP_DEBUGF(TCP_DEBUG, ("| %010"U32_F" | (ack no)\n",
+ lwip_ntohl(tcphdr->ackno)));
+ LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n"));
+ LWIP_DEBUGF(TCP_DEBUG, ("| %2"U16_F" | |%"U16_F"%"U16_F"%"U16_F"%"U16_F"%"U16_F"%"U16_F"| %5"U16_F" | (hdrlen, flags (",
+ TCPH_HDRLEN(tcphdr),
+ (u16_t)(TCPH_FLAGS(tcphdr) >> 5 & 1),
+ (u16_t)(TCPH_FLAGS(tcphdr) >> 4 & 1),
+ (u16_t)(TCPH_FLAGS(tcphdr) >> 3 & 1),
+ (u16_t)(TCPH_FLAGS(tcphdr) >> 2 & 1),
+ (u16_t)(TCPH_FLAGS(tcphdr) >> 1 & 1),
+ (u16_t)(TCPH_FLAGS(tcphdr) & 1),
+ lwip_ntohs(tcphdr->wnd)));
+ tcp_debug_print_flags(TCPH_FLAGS(tcphdr));
+ LWIP_DEBUGF(TCP_DEBUG, ("), win)\n"));
+ LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n"));
+ LWIP_DEBUGF(TCP_DEBUG, ("| 0x%04"X16_F" | %5"U16_F" | (chksum, urgp)\n",
+ lwip_ntohs(tcphdr->chksum), lwip_ntohs(tcphdr->urgp)));
+ LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n"));
+}
+
+/**
+ * Print a tcp state for debugging purposes.
+ *
+ * @param s enum tcp_state to print
+ */
+void
+tcp_debug_print_state(enum tcp_state s)
+{
+ LWIP_DEBUGF(TCP_DEBUG, ("State: %s\n", tcp_state_str[s]));
+}
+
+/**
+ * Print tcp flags for debugging purposes.
+ *
+ * @param flags tcp flags, all active flags are printed
+ */
+void
+tcp_debug_print_flags(u8_t flags)
+{
+ if (flags & TCP_FIN) {
+ LWIP_DEBUGF(TCP_DEBUG, ("FIN "));
+ }
+ if (flags & TCP_SYN) {
+ LWIP_DEBUGF(TCP_DEBUG, ("SYN "));
+ }
+ if (flags & TCP_RST) {
+ LWIP_DEBUGF(TCP_DEBUG, ("RST "));
+ }
+ if (flags & TCP_PSH) {
+ LWIP_DEBUGF(TCP_DEBUG, ("PSH "));
+ }
+ if (flags & TCP_ACK) {
+ LWIP_DEBUGF(TCP_DEBUG, ("ACK "));
+ }
+ if (flags & TCP_URG) {
+ LWIP_DEBUGF(TCP_DEBUG, ("URG "));
+ }
+ if (flags & TCP_ECE) {
+ LWIP_DEBUGF(TCP_DEBUG, ("ECE "));
+ }
+ if (flags & TCP_CWR) {
+ LWIP_DEBUGF(TCP_DEBUG, ("CWR "));
+ }
+ LWIP_DEBUGF(TCP_DEBUG, ("\n"));
+}
+
+/**
+ * Print all tcp_pcbs in every list for debugging purposes.
+ */
+void
+tcp_debug_print_pcbs(void)
+{
+ struct tcp_pcb *pcb;
+ struct tcp_pcb_listen *pcbl;
+
+ LWIP_DEBUGF(TCP_DEBUG, ("Active PCB states:\n"));
+ for (pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) {
+ LWIP_DEBUGF(TCP_DEBUG, ("Local port %"U16_F", foreign port %"U16_F" snd_nxt %"U32_F" rcv_nxt %"U32_F" ",
+ pcb->local_port, pcb->remote_port,
+ pcb->snd_nxt, pcb->rcv_nxt));
+ tcp_debug_print_state(pcb->state);
+ }
+
+ LWIP_DEBUGF(TCP_DEBUG, ("Listen PCB states:\n"));
+ for (pcbl = tcp_listen_pcbs.listen_pcbs; pcbl != NULL; pcbl = pcbl->next) {
+ LWIP_DEBUGF(TCP_DEBUG, ("Local port %"U16_F" ", pcbl->local_port));
+ tcp_debug_print_state(pcbl->state);
+ }
+
+ LWIP_DEBUGF(TCP_DEBUG, ("TIME-WAIT PCB states:\n"));
+ for (pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) {
+ LWIP_DEBUGF(TCP_DEBUG, ("Local port %"U16_F", foreign port %"U16_F" snd_nxt %"U32_F" rcv_nxt %"U32_F" ",
+ pcb->local_port, pcb->remote_port,
+ pcb->snd_nxt, pcb->rcv_nxt));
+ tcp_debug_print_state(pcb->state);
+ }
+}
+
+/**
+ * Check state consistency of the tcp_pcb lists.
+ */
+s16_t
+tcp_pcbs_sane(void)
+{
+ struct tcp_pcb *pcb;
+ for (pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) {
+ LWIP_ASSERT("tcp_pcbs_sane: active pcb->state != CLOSED", pcb->state != CLOSED);
+ LWIP_ASSERT("tcp_pcbs_sane: active pcb->state != LISTEN", pcb->state != LISTEN);
+ LWIP_ASSERT("tcp_pcbs_sane: active pcb->state != TIME-WAIT", pcb->state != TIME_WAIT);
+ }
+ for (pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) {
+ LWIP_ASSERT("tcp_pcbs_sane: tw pcb->state == TIME-WAIT", pcb->state == TIME_WAIT);
+ }
+ return 1;
+}
+#endif /* TCP_DEBUG */
+
+#endif /* LWIP_TCP */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/tcp_in.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/tcp_in.c
new file mode 100644
index 0000000..ba87928
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/tcp_in.c
@@ -0,0 +1,1818 @@
+/**
+ * @file
+ * Transmission Control Protocol, incoming traffic
+ *
+ * The input processing functions of the TCP layer.
+ *
+ * These functions are generally called in the order (ip_input() ->)
+ * tcp_input() -> * tcp_process() -> tcp_receive() (-> application).
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_TCP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/priv/tcp_priv.h"
+#include "lwip/def.h"
+#include "lwip/ip_addr.h"
+#include "lwip/netif.h"
+#include "lwip/mem.h"
+#include "lwip/memp.h"
+#include "lwip/inet_chksum.h"
+#include "lwip/stats.h"
+#include "lwip/ip6.h"
+#include "lwip/ip6_addr.h"
+#if LWIP_ND6_TCP_REACHABILITY_HINTS
+#include "lwip/nd6.h"
+#endif /* LWIP_ND6_TCP_REACHABILITY_HINTS */
+
+/** Initial CWND calculation as defined RFC 2581 */
+#define LWIP_TCP_CALC_INITIAL_CWND(mss) LWIP_MIN((4U * (mss)), LWIP_MAX((2U * (mss)), 4380U));
+
+/* These variables are global to all functions involved in the input
+ processing of TCP segments. They are set by the tcp_input()
+ function. */
+static struct tcp_seg inseg;
+static struct tcp_hdr *tcphdr;
+static u16_t tcphdr_optlen;
+static u16_t tcphdr_opt1len;
+static u8_t* tcphdr_opt2;
+static u16_t tcp_optidx;
+static u32_t seqno, ackno;
+static tcpwnd_size_t recv_acked;
+static u16_t tcplen;
+static u8_t flags;
+
+static u8_t recv_flags;
+static struct pbuf *recv_data;
+
+struct tcp_pcb *tcp_input_pcb;
+
+/* Forward declarations. */
+static err_t tcp_process(struct tcp_pcb *pcb);
+static void tcp_receive(struct tcp_pcb *pcb);
+static void tcp_parseopt(struct tcp_pcb *pcb);
+
+static void tcp_listen_input(struct tcp_pcb_listen *pcb);
+static void tcp_timewait_input(struct tcp_pcb *pcb);
+
+/**
+ * The initial input processing of TCP. It verifies the TCP header, demultiplexes
+ * the segment between the PCBs and passes it on to tcp_process(), which implements
+ * the TCP finite state machine. This function is called by the IP layer (in
+ * ip_input()).
+ *
+ * @param p received TCP segment to process (p->payload pointing to the TCP header)
+ * @param inp network interface on which this segment was received
+ */
+void
+tcp_input(struct pbuf *p, struct netif *inp)
+{
+ struct tcp_pcb *pcb, *prev;
+ struct tcp_pcb_listen *lpcb;
+#if SO_REUSE
+ struct tcp_pcb *lpcb_prev = NULL;
+ struct tcp_pcb_listen *lpcb_any = NULL;
+#endif /* SO_REUSE */
+ u8_t hdrlen_bytes;
+ err_t err;
+
+ LWIP_UNUSED_ARG(inp);
+
+ PERF_START;
+
+ TCP_STATS_INC(tcp.recv);
+ MIB2_STATS_INC(mib2.tcpinsegs);
+
+ tcphdr = (struct tcp_hdr *)p->payload;
+
+#if TCP_INPUT_DEBUG
+ tcp_debug_print(tcphdr);
+#endif
+
+ /* Check that TCP header fits in payload */
+ if (p->len < TCP_HLEN) {
+ /* drop short packets */
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: short packet (%"U16_F" bytes) discarded\n", p->tot_len));
+ TCP_STATS_INC(tcp.lenerr);
+ goto dropped;
+ }
+
+ /* Don't even process incoming broadcasts/multicasts. */
+ if (ip_addr_isbroadcast(ip_current_dest_addr(), ip_current_netif()) ||
+ ip_addr_ismulticast(ip_current_dest_addr())) {
+ TCP_STATS_INC(tcp.proterr);
+ goto dropped;
+ }
+
+#if CHECKSUM_CHECK_TCP
+ IF__NETIF_CHECKSUM_ENABLED(inp, NETIF_CHECKSUM_CHECK_TCP) {
+ /* Verify TCP checksum. */
+ u16_t chksum = ip_chksum_pseudo(p, IP_PROTO_TCP, p->tot_len,
+ ip_current_src_addr(), ip_current_dest_addr());
+ if (chksum != 0) {
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packet discarded due to failing checksum 0x%04"X16_F"\n",
+ chksum));
+ tcp_debug_print(tcphdr);
+ TCP_STATS_INC(tcp.chkerr);
+ goto dropped;
+ }
+ }
+#endif /* CHECKSUM_CHECK_TCP */
+
+ /* sanity-check header length */
+ hdrlen_bytes = TCPH_HDRLEN(tcphdr) * 4;
+ if ((hdrlen_bytes < TCP_HLEN) || (hdrlen_bytes > p->tot_len)) {
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: invalid header length (%"U16_F")\n", (u16_t)hdrlen_bytes));
+ TCP_STATS_INC(tcp.lenerr);
+ goto dropped;
+ }
+
+ /* Move the payload pointer in the pbuf so that it points to the
+ TCP data instead of the TCP header. */
+ tcphdr_optlen = hdrlen_bytes - TCP_HLEN;
+ tcphdr_opt2 = NULL;
+ if (p->len >= hdrlen_bytes) {
+ /* all options are in the first pbuf */
+ tcphdr_opt1len = tcphdr_optlen;
+ pbuf_header(p, -(s16_t)hdrlen_bytes); /* cannot fail */
+ } else {
+ u16_t opt2len;
+ /* TCP header fits into first pbuf, options don't - data is in the next pbuf */
+ /* there must be a next pbuf, due to hdrlen_bytes sanity check above */
+ LWIP_ASSERT("p->next != NULL", p->next != NULL);
+
+ /* advance over the TCP header (cannot fail) */
+ pbuf_header(p, -TCP_HLEN);
+
+ /* determine how long the first and second parts of the options are */
+ tcphdr_opt1len = p->len;
+ opt2len = tcphdr_optlen - tcphdr_opt1len;
+
+ /* options continue in the next pbuf: set p to zero length and hide the
+ options in the next pbuf (adjusting p->tot_len) */
+ pbuf_header(p, -(s16_t)tcphdr_opt1len);
+
+ /* check that the options fit in the second pbuf */
+ if (opt2len > p->next->len) {
+ /* drop short packets */
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: options overflow second pbuf (%"U16_F" bytes)\n", p->next->len));
+ TCP_STATS_INC(tcp.lenerr);
+ goto dropped;
+ }
+
+ /* remember the pointer to the second part of the options */
+ tcphdr_opt2 = (u8_t*)p->next->payload;
+
+ /* advance p->next to point after the options, and manually
+ adjust p->tot_len to keep it consistent with the changed p->next */
+ pbuf_header(p->next, -(s16_t)opt2len);
+ p->tot_len -= opt2len;
+
+ LWIP_ASSERT("p->len == 0", p->len == 0);
+ LWIP_ASSERT("p->tot_len == p->next->tot_len", p->tot_len == p->next->tot_len);
+ }
+
+ /* Convert fields in TCP header to host byte order. */
+ tcphdr->src = lwip_ntohs(tcphdr->src);
+ tcphdr->dest = lwip_ntohs(tcphdr->dest);
+ seqno = tcphdr->seqno = lwip_ntohl(tcphdr->seqno);
+ ackno = tcphdr->ackno = lwip_ntohl(tcphdr->ackno);
+ tcphdr->wnd = lwip_ntohs(tcphdr->wnd);
+
+ flags = TCPH_FLAGS(tcphdr);
+ tcplen = p->tot_len + ((flags & (TCP_FIN | TCP_SYN)) ? 1 : 0);
+
+ /* Demultiplex an incoming segment. First, we check if it is destined
+ for an active connection. */
+ prev = NULL;
+
+ for (pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) {
+ LWIP_ASSERT("tcp_input: active pcb->state != CLOSED", pcb->state != CLOSED);
+ LWIP_ASSERT("tcp_input: active pcb->state != TIME-WAIT", pcb->state != TIME_WAIT);
+ LWIP_ASSERT("tcp_input: active pcb->state != LISTEN", pcb->state != LISTEN);
+ if (pcb->remote_port == tcphdr->src &&
+ pcb->local_port == tcphdr->dest &&
+ ip_addr_cmp(&pcb->remote_ip, ip_current_src_addr()) &&
+ ip_addr_cmp(&pcb->local_ip, ip_current_dest_addr())) {
+ /* Move this PCB to the front of the list so that subsequent
+ lookups will be faster (we exploit locality in TCP segment
+ arrivals). */
+ LWIP_ASSERT("tcp_input: pcb->next != pcb (before cache)", pcb->next != pcb);
+ if (prev != NULL) {
+ prev->next = pcb->next;
+ pcb->next = tcp_active_pcbs;
+ tcp_active_pcbs = pcb;
+ } else {
+ TCP_STATS_INC(tcp.cachehit);
+ }
+ LWIP_ASSERT("tcp_input: pcb->next != pcb (after cache)", pcb->next != pcb);
+ break;
+ }
+ prev = pcb;
+ }
+
+ if (pcb == NULL) {
+ /* If it did not go to an active connection, we check the connections
+ in the TIME-WAIT state. */
+ for (pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) {
+ LWIP_ASSERT("tcp_input: TIME-WAIT pcb->state == TIME-WAIT", pcb->state == TIME_WAIT);
+ if (pcb->remote_port == tcphdr->src &&
+ pcb->local_port == tcphdr->dest &&
+ ip_addr_cmp(&pcb->remote_ip, ip_current_src_addr()) &&
+ ip_addr_cmp(&pcb->local_ip, ip_current_dest_addr())) {
+ /* We don't really care enough to move this PCB to the front
+ of the list since we are not very likely to receive that
+ many segments for connections in TIME-WAIT. */
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packed for TIME_WAITing connection.\n"));
+ tcp_timewait_input(pcb);
+ pbuf_free(p);
+ return;
+ }
+ }
+
+ /* Finally, if we still did not get a match, we check all PCBs that
+ are LISTENing for incoming connections. */
+ prev = NULL;
+ for (lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) {
+ if (lpcb->local_port == tcphdr->dest) {
+ if (IP_IS_ANY_TYPE_VAL(lpcb->local_ip)) {
+ /* found an ANY TYPE (IPv4/IPv6) match */
+#if SO_REUSE
+ lpcb_any = lpcb;
+ lpcb_prev = prev;
+#else /* SO_REUSE */
+ break;
+#endif /* SO_REUSE */
+ } else if (IP_ADDR_PCB_VERSION_MATCH_EXACT(lpcb, ip_current_dest_addr())) {
+ if (ip_addr_cmp(&lpcb->local_ip, ip_current_dest_addr())) {
+ /* found an exact match */
+ break;
+ } else if (ip_addr_isany(&lpcb->local_ip)) {
+ /* found an ANY-match */
+#if SO_REUSE
+ lpcb_any = lpcb;
+ lpcb_prev = prev;
+#else /* SO_REUSE */
+ break;
+ #endif /* SO_REUSE */
+ }
+ }
+ }
+ prev = (struct tcp_pcb *)lpcb;
+ }
+#if SO_REUSE
+ /* first try specific local IP */
+ if (lpcb == NULL) {
+ /* only pass to ANY if no specific local IP has been found */
+ lpcb = lpcb_any;
+ prev = lpcb_prev;
+ }
+#endif /* SO_REUSE */
+ if (lpcb != NULL) {
+ /* Move this PCB to the front of the list so that subsequent
+ lookups will be faster (we exploit locality in TCP segment
+ arrivals). */
+ if (prev != NULL) {
+ ((struct tcp_pcb_listen *)prev)->next = lpcb->next;
+ /* our successor is the remainder of the listening list */
+ lpcb->next = tcp_listen_pcbs.listen_pcbs;
+ /* put this listening pcb at the head of the listening list */
+ tcp_listen_pcbs.listen_pcbs = lpcb;
+ } else {
+ TCP_STATS_INC(tcp.cachehit);
+ }
+
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packed for LISTENing connection.\n"));
+ tcp_listen_input(lpcb);
+ pbuf_free(p);
+ return;
+ }
+ }
+
+#if TCP_INPUT_DEBUG
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("+-+-+-+-+-+-+-+-+-+-+-+-+-+- tcp_input: flags "));
+ tcp_debug_print_flags(TCPH_FLAGS(tcphdr));
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n"));
+#endif /* TCP_INPUT_DEBUG */
+
+
+ if (pcb != NULL) {
+ /* The incoming segment belongs to a connection. */
+#if TCP_INPUT_DEBUG
+ tcp_debug_print_state(pcb->state);
+#endif /* TCP_INPUT_DEBUG */
+
+ /* Set up a tcp_seg structure. */
+ inseg.next = NULL;
+ inseg.len = p->tot_len;
+ inseg.p = p;
+ inseg.tcphdr = tcphdr;
+
+ recv_data = NULL;
+ recv_flags = 0;
+ recv_acked = 0;
+
+ if (flags & TCP_PSH) {
+ p->flags |= PBUF_FLAG_PUSH;
+ }
+
+ /* If there is data which was previously "refused" by upper layer */
+ if (pcb->refused_data != NULL) {
+ if ((tcp_process_refused_data(pcb) == ERR_ABRT) ||
+ ((pcb->refused_data != NULL) && (tcplen > 0))) {
+ /* pcb has been aborted or refused data is still refused and the new
+ segment contains data */
+ if (pcb->rcv_ann_wnd == 0) {
+ /* this is a zero-window probe, we respond to it with current RCV.NXT
+ and drop the data segment */
+ tcp_send_empty_ack(pcb);
+ }
+ TCP_STATS_INC(tcp.drop);
+ MIB2_STATS_INC(mib2.tcpinerrs);
+ goto aborted;
+ }
+ }
+ tcp_input_pcb = pcb;
+ err = tcp_process(pcb);
+ /* A return value of ERR_ABRT means that tcp_abort() was called
+ and that the pcb has been freed. If so, we don't do anything. */
+ if (err != ERR_ABRT) {
+ if (recv_flags & TF_RESET) {
+ /* TF_RESET means that the connection was reset by the other
+ end. We then call the error callback to inform the
+ application that the connection is dead before we
+ deallocate the PCB. */
+ TCP_EVENT_ERR(pcb->state, pcb->errf, pcb->callback_arg, ERR_RST);
+ tcp_pcb_remove(&tcp_active_pcbs, pcb);
+ memp_free(MEMP_TCP_PCB, pcb);
+ } else {
+ err = ERR_OK;
+ /* If the application has registered a "sent" function to be
+ called when new send buffer space is available, we call it
+ now. */
+ if (recv_acked > 0) {
+ u16_t acked16;
+#if LWIP_WND_SCALE
+ /* recv_acked is u32_t but the sent callback only takes a u16_t,
+ so we might have to call it multiple times. */
+ u32_t acked = recv_acked;
+ while (acked > 0) {
+ acked16 = (u16_t)LWIP_MIN(acked, 0xffffu);
+ acked -= acked16;
+#else
+ {
+ acked16 = recv_acked;
+#endif
+ TCP_EVENT_SENT(pcb, (u16_t)acked16, err);
+ if (err == ERR_ABRT) {
+ goto aborted;
+ }
+ }
+ recv_acked = 0;
+ }
+ 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->state, pcb->errf, pcb->callback_arg, ERR_CLSD);
+ }
+ tcp_pcb_remove(&tcp_active_pcbs, pcb);
+ memp_free(MEMP_TCP_PCB, pcb);
+ goto aborted;
+ }
+#if TCP_QUEUE_OOSEQ && LWIP_WND_SCALE
+ while (recv_data != NULL) {
+ struct pbuf *rest = NULL;
+ pbuf_split_64k(recv_data, &rest);
+#else /* TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */
+ if (recv_data != NULL) {
+#endif /* TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */
+
+ LWIP_ASSERT("pcb->refused_data == NULL", pcb->refused_data == NULL);
+ if (pcb->flags & TF_RXCLOSED) {
+ /* received data although already closed -> abort (send RST) to
+ notify the remote host that not all data has been processed */
+ pbuf_free(recv_data);
+#if TCP_QUEUE_OOSEQ && LWIP_WND_SCALE
+ if (rest != NULL) {
+ pbuf_free(rest);
+ }
+#endif /* TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */
+ tcp_abort(pcb);
+ goto aborted;
+ }
+
+ /* Notify application that data has been received. */
+ TCP_EVENT_RECV(pcb, recv_data, ERR_OK, err);
+ if (err == ERR_ABRT) {
+#if TCP_QUEUE_OOSEQ && LWIP_WND_SCALE
+ if (rest != NULL) {
+ pbuf_free(rest);
+ }
+#endif /* TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */
+ goto aborted;
+ }
+
+ /* If the upper layer can't receive this data, store it */
+ if (err != ERR_OK) {
+#if TCP_QUEUE_OOSEQ && LWIP_WND_SCALE
+ if (rest != NULL) {
+ pbuf_cat(recv_data, rest);
+ }
+#endif /* TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */
+ pcb->refused_data = recv_data;
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: keep incoming packet, because pcb is \"full\"\n"));
+#if TCP_QUEUE_OOSEQ && LWIP_WND_SCALE
+ break;
+ } else {
+ /* Upper layer received the data, go on with the rest if > 64K */
+ recv_data = rest;
+#endif /* TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */
+ }
+ }
+
+ /* If a FIN segment was received, we call the callback
+ function with a NULL buffer to indicate EOF. */
+ if (recv_flags & TF_GOT_FIN) {
+ if (pcb->refused_data != NULL) {
+ /* Delay this if we have refused data. */
+ pcb->refused_data->flags |= PBUF_FLAG_TCP_FIN;
+ } else {
+ /* correct rcv_wnd as the application won't call tcp_recved()
+ for the FIN's seqno */
+ if (pcb->rcv_wnd != TCP_WND_MAX(pcb)) {
+ pcb->rcv_wnd++;
+ }
+ TCP_EVENT_CLOSED(pcb, err);
+ if (err == ERR_ABRT) {
+ goto aborted;
+ }
+ }
+ }
+
+ tcp_input_pcb = NULL;
+ /* Try to send something out. */
+ tcp_output(pcb);
+#if TCP_INPUT_DEBUG
+#if TCP_DEBUG
+ tcp_debug_print_state(pcb->state);
+#endif /* TCP_DEBUG */
+#endif /* TCP_INPUT_DEBUG */
+ }
+ }
+ /* Jump target if pcb has been aborted in a callback (by calling tcp_abort()).
+ Below this line, 'pcb' may not be dereferenced! */
+aborted:
+ tcp_input_pcb = NULL;
+ recv_data = NULL;
+
+ /* give up our reference to inseg.p */
+ if (inseg.p != NULL)
+ {
+ pbuf_free(inseg.p);
+ inseg.p = NULL;
+ }
+ } else {
+
+ /* If no matching PCB was found, send a TCP RST (reset) to the
+ sender. */
+ LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_input: no PCB match found, resetting.\n"));
+ if (!(TCPH_FLAGS(tcphdr) & TCP_RST)) {
+ TCP_STATS_INC(tcp.proterr);
+ TCP_STATS_INC(tcp.drop);
+ tcp_rst(ackno, seqno + tcplen, ip_current_dest_addr(),
+ ip_current_src_addr(), tcphdr->dest, tcphdr->src);
+ }
+ pbuf_free(p);
+ }
+
+ LWIP_ASSERT("tcp_input: tcp_pcbs_sane()", tcp_pcbs_sane());
+ PERF_STOP("tcp_input");
+ return;
+dropped:
+ TCP_STATS_INC(tcp.drop);
+ MIB2_STATS_INC(mib2.tcpinerrs);
+ pbuf_free(p);
+}
+
+/**
+ * Called by tcp_input() when a segment arrives for a listening
+ * connection (from tcp_input()).
+ *
+ * @param pcb the tcp_pcb_listen for which a segment arrived
+ *
+ * @note the segment which arrived is saved in global variables, therefore only the pcb
+ * involved is passed as a parameter to this function
+ */
+static void
+tcp_listen_input(struct tcp_pcb_listen *pcb)
+{
+ struct tcp_pcb *npcb;
+ u32_t iss;
+ err_t rc;
+
+ if (flags & TCP_RST) {
+ /* An incoming RST should be ignored. Return. */
+ return;
+ }
+
+ /* In the LISTEN state, we check for incoming SYN segments,
+ creates a new PCB, and responds with a SYN|ACK. */
+ if (flags & TCP_ACK) {
+ /* For incoming segments with the ACK flag set, respond with a
+ RST. */
+ LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_listen_input: ACK in LISTEN, sending reset\n"));
+ tcp_rst(ackno, seqno + tcplen, ip_current_dest_addr(),
+ ip_current_src_addr(), tcphdr->dest, tcphdr->src);
+ } else if (flags & TCP_SYN) {
+ LWIP_DEBUGF(TCP_DEBUG, ("TCP connection request %"U16_F" -> %"U16_F".\n", tcphdr->src, tcphdr->dest));
+#if TCP_LISTEN_BACKLOG
+ if (pcb->accepts_pending >= pcb->backlog) {
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_listen_input: listen backlog exceeded for port %"U16_F"\n", tcphdr->dest));
+ return;
+ }
+#endif /* TCP_LISTEN_BACKLOG */
+ npcb = tcp_alloc(pcb->prio);
+ /* If a new PCB could not be created (probably due to lack of memory),
+ we don't do anything, but rely on the sender will retransmit the
+ SYN at a time when we have more memory available. */
+ if (npcb == NULL) {
+ err_t err;
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_listen_input: could not allocate PCB\n"));
+ TCP_STATS_INC(tcp.memerr);
+ TCP_EVENT_ACCEPT(pcb, NULL, pcb->callback_arg, ERR_MEM, err);
+ LWIP_UNUSED_ARG(err); /* err not useful here */
+ return;
+ }
+#if TCP_LISTEN_BACKLOG
+ pcb->accepts_pending++;
+ npcb->flags |= TF_BACKLOGPEND;
+#endif /* TCP_LISTEN_BACKLOG */
+ /* Set up the new PCB. */
+ ip_addr_copy(npcb->local_ip, *ip_current_dest_addr());
+ ip_addr_copy(npcb->remote_ip, *ip_current_src_addr());
+ npcb->local_port = pcb->local_port;
+ npcb->remote_port = tcphdr->src;
+ npcb->state = SYN_RCVD;
+ npcb->rcv_nxt = seqno + 1;
+ npcb->rcv_ann_right_edge = npcb->rcv_nxt;
+ iss = tcp_next_iss(npcb);
+ npcb->snd_wl2 = iss;
+ npcb->snd_nxt = iss;
+ npcb->lastack = iss;
+ npcb->snd_lbb = iss;
+ npcb->snd_wl1 = seqno - 1;/* initialise to seqno-1 to force window update */
+ npcb->callback_arg = pcb->callback_arg;
+#if LWIP_CALLBACK_API || TCP_LISTEN_BACKLOG
+ npcb->listener = pcb;
+#endif /* LWIP_CALLBACK_API || TCP_LISTEN_BACKLOG */
+ /* inherit socket options */
+ npcb->so_options = pcb->so_options & SOF_INHERITED;
+ /* Register the new PCB so that we can begin receiving segments
+ for it. */
+ TCP_REG_ACTIVE(npcb);
+
+ /* Parse any options in the SYN. */
+ tcp_parseopt(npcb);
+ npcb->snd_wnd = tcphdr->wnd;
+ npcb->snd_wnd_max = npcb->snd_wnd;
+
+#if TCP_CALCULATE_EFF_SEND_MSS
+ npcb->mss = tcp_eff_send_mss(npcb->mss, &npcb->local_ip, &npcb->remote_ip);
+#endif /* TCP_CALCULATE_EFF_SEND_MSS */
+
+ MIB2_STATS_INC(mib2.tcppassiveopens);
+
+ /* Send a SYN|ACK together with the MSS option. */
+ rc = tcp_enqueue_flags(npcb, TCP_SYN | TCP_ACK);
+ if (rc != ERR_OK) {
+ tcp_abandon(npcb, 0);
+ return;
+ }
+ tcp_output(npcb);
+ }
+ return;
+}
+
+/**
+ * Called by tcp_input() when a segment arrives for a connection in
+ * TIME_WAIT.
+ *
+ * @param pcb the tcp_pcb for which a segment arrived
+ *
+ * @note the segment which arrived is saved in global variables, therefore only the pcb
+ * involved is passed as a parameter to this function
+ */
+static void
+tcp_timewait_input(struct tcp_pcb *pcb)
+{
+ /* RFC 1337: in TIME_WAIT, ignore RST and ACK FINs + any 'acceptable' segments */
+ /* RFC 793 3.9 Event Processing - Segment Arrives:
+ * - first check sequence number - we skip that one in TIME_WAIT (always
+ * acceptable since we only send ACKs)
+ * - second check the RST bit (... return) */
+ if (flags & TCP_RST) {
+ return;
+ }
+ /* - fourth, check the SYN bit, */
+ if (flags & TCP_SYN) {
+ /* If an incoming segment is not acceptable, an acknowledgment
+ should be sent in reply */
+ if (TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, pcb->rcv_nxt + pcb->rcv_wnd)) {
+ /* If the SYN is in the window it is an error, send a reset */
+ tcp_rst(ackno, seqno + tcplen, ip_current_dest_addr(),
+ ip_current_src_addr(), tcphdr->dest, tcphdr->src);
+ return;
+ }
+ } else if (flags & TCP_FIN) {
+ /* - eighth, check the FIN bit: Remain in the TIME-WAIT state.
+ Restart the 2 MSL time-wait timeout.*/
+ pcb->tmr = tcp_ticks;
+ }
+
+ if ((tcplen > 0)) {
+ /* Acknowledge data, FIN or out-of-window SYN */
+ pcb->flags |= TF_ACK_NOW;
+ tcp_output(pcb);
+ }
+ return;
+}
+
+/**
+ * Implements the TCP state machine. Called by tcp_input. In some
+ * states tcp_receive() is called to receive data. The tcp_seg
+ * argument will be freed by the caller (tcp_input()) unless the
+ * recv_data pointer in the pcb is set.
+ *
+ * @param pcb the tcp_pcb for which a segment arrived
+ *
+ * @note the segment which arrived is saved in global variables, therefore only the pcb
+ * involved is passed as a parameter to this function
+ */
+static err_t
+tcp_process(struct tcp_pcb *pcb)
+{
+ struct tcp_seg *rseg;
+ u8_t acceptable = 0;
+ err_t err;
+
+ err = ERR_OK;
+
+ /* Process incoming RST segments. */
+ if (flags & TCP_RST) {
+ /* First, determine if the reset is acceptable. */
+ if (pcb->state == SYN_SENT) {
+ /* "In the SYN-SENT state (a RST received in response to an initial SYN),
+ the RST is acceptable if the ACK field acknowledges the SYN." */
+ if (ackno == pcb->snd_nxt) {
+ acceptable = 1;
+ }
+ } else {
+ /* "In all states except SYN-SENT, all reset (RST) segments are validated
+ by checking their SEQ-fields." */
+ if (seqno == pcb->rcv_nxt) {
+ acceptable = 1;
+ } else if (TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt,
+ pcb->rcv_nxt + pcb->rcv_wnd)) {
+ /* If the sequence number is inside the window, we only send an ACK
+ and wait for a re-send with matching sequence number.
+ This violates RFC 793, but is required to protection against
+ CVE-2004-0230 (RST spoofing attack). */
+ tcp_ack_now(pcb);
+ }
+ }
+
+ if (acceptable) {
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_process: Connection RESET\n"));
+ LWIP_ASSERT("tcp_input: pcb->state != CLOSED", pcb->state != CLOSED);
+ recv_flags |= TF_RESET;
+ pcb->flags &= ~TF_ACK_DELAY;
+ return ERR_RST;
+ } else {
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_process: unacceptable reset seqno %"U32_F" rcv_nxt %"U32_F"\n",
+ seqno, pcb->rcv_nxt));
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_process: unacceptable reset seqno %"U32_F" rcv_nxt %"U32_F"\n",
+ seqno, pcb->rcv_nxt));
+ return ERR_OK;
+ }
+ }
+
+ if ((flags & TCP_SYN) && (pcb->state != SYN_SENT && pcb->state != SYN_RCVD)) {
+ /* Cope with new connection attempt after remote end crashed */
+ tcp_ack_now(pcb);
+ return ERR_OK;
+ }
+
+ if ((pcb->flags & TF_RXCLOSED) == 0) {
+ /* Update the PCB (in)activity timer unless rx is closed (see tcp_shutdown) */
+ pcb->tmr = tcp_ticks;
+ }
+ pcb->keep_cnt_sent = 0;
+
+ tcp_parseopt(pcb);
+
+ /* Do different things depending on the TCP state. */
+ switch (pcb->state) {
+ case SYN_SENT:
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("SYN-SENT: ackno %"U32_F" pcb->snd_nxt %"U32_F" unacked %"U32_F"\n", ackno,
+ pcb->snd_nxt, lwip_ntohl(pcb->unacked->tcphdr->seqno)));
+ /* received SYN ACK with expected sequence number? */
+ if ((flags & TCP_ACK) && (flags & TCP_SYN)
+ && (ackno == pcb->lastack + 1)) {
+ pcb->rcv_nxt = seqno + 1;
+ pcb->rcv_ann_right_edge = pcb->rcv_nxt;
+ pcb->lastack = ackno;
+ pcb->snd_wnd = tcphdr->wnd;
+ pcb->snd_wnd_max = pcb->snd_wnd;
+ pcb->snd_wl1 = seqno - 1; /* initialise to seqno - 1 to force window update */
+ pcb->state = ESTABLISHED;
+
+#if TCP_CALCULATE_EFF_SEND_MSS
+ pcb->mss = tcp_eff_send_mss(pcb->mss, &pcb->local_ip, &pcb->remote_ip);
+#endif /* TCP_CALCULATE_EFF_SEND_MSS */
+
+ pcb->cwnd = LWIP_TCP_CALC_INITIAL_CWND(pcb->mss);
+ LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_process (SENT): cwnd %"TCPWNDSIZE_F
+ " ssthresh %"TCPWNDSIZE_F"\n",
+ pcb->cwnd, pcb->ssthresh));
+ LWIP_ASSERT("pcb->snd_queuelen > 0", (pcb->snd_queuelen > 0));
+ --pcb->snd_queuelen;
+ LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_process: SYN-SENT --queuelen %"TCPWNDSIZE_F"\n", (tcpwnd_size_t)pcb->snd_queuelen));
+ rseg = pcb->unacked;
+ if (rseg == NULL) {
+ /* might happen if tcp_output fails in tcp_rexmit_rto()
+ in which case the segment is on the unsent list */
+ rseg = pcb->unsent;
+ LWIP_ASSERT("no segment to free", rseg != NULL);
+ pcb->unsent = rseg->next;
+ } else {
+ pcb->unacked = rseg->next;
+ }
+ tcp_seg_free(rseg);
+
+ /* If there's nothing left to acknowledge, stop the retransmit
+ timer, otherwise reset it to start again */
+ if (pcb->unacked == NULL) {
+ pcb->rtime = -1;
+ } else {
+ pcb->rtime = 0;
+ pcb->nrtx = 0;
+ }
+
+ /* Call the user specified function to call when successfully
+ * connected. */
+ TCP_EVENT_CONNECTED(pcb, ERR_OK, err);
+ if (err == ERR_ABRT) {
+ return ERR_ABRT;
+ }
+ tcp_ack_now(pcb);
+ }
+ /* received ACK? possibly a half-open connection */
+ else if (flags & TCP_ACK) {
+ /* send a RST to bring the other side in a non-synchronized state. */
+ tcp_rst(ackno, seqno + tcplen, ip_current_dest_addr(),
+ ip_current_src_addr(), tcphdr->dest, tcphdr->src);
+ /* Resend SYN immediately (don't wait for rto timeout) to establish
+ connection faster, but do not send more SYNs than we otherwise would
+ have, or we might get caught in a loop on loopback interfaces. */
+ if (pcb->nrtx < TCP_SYNMAXRTX) {
+ pcb->rtime = 0;
+ tcp_rexmit_rto(pcb);
+ }
+ }
+ break;
+ case SYN_RCVD:
+ if (flags & TCP_ACK) {
+ /* expected ACK number? */
+ if (TCP_SEQ_BETWEEN(ackno, pcb->lastack+1, pcb->snd_nxt)) {
+ pcb->state = ESTABLISHED;
+ LWIP_DEBUGF(TCP_DEBUG, ("TCP connection established %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
+#if LWIP_CALLBACK_API || TCP_LISTEN_BACKLOG
+#if LWIP_CALLBACK_API
+ LWIP_ASSERT("pcb->listener->accept != NULL",
+ (pcb->listener == NULL) || (pcb->listener->accept != NULL));
+#endif
+ if (pcb->listener == NULL) {
+ /* listen pcb might be closed by now */
+ err = ERR_VAL;
+ } else
+#endif /* LWIP_CALLBACK_API || TCP_LISTEN_BACKLOG */
+ {
+ tcp_backlog_accepted(pcb);
+ /* Call the accept function. */
+ TCP_EVENT_ACCEPT(pcb->listener, pcb, pcb->callback_arg, ERR_OK, err);
+ }
+ if (err != ERR_OK) {
+ /* If the accept function returns with an error, we abort
+ * the connection. */
+ /* Already aborted? */
+ if (err != ERR_ABRT) {
+ tcp_abort(pcb);
+ }
+ return ERR_ABRT;
+ }
+ /* If there was any data contained within this ACK,
+ * we'd better pass it on to the application as well. */
+ tcp_receive(pcb);
+
+ /* Prevent ACK for SYN to generate a sent event */
+ if (recv_acked != 0) {
+ recv_acked--;
+ }
+
+ pcb->cwnd = LWIP_TCP_CALC_INITIAL_CWND(pcb->mss);
+ LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_process (SYN_RCVD): cwnd %"TCPWNDSIZE_F
+ " ssthresh %"TCPWNDSIZE_F"\n",
+ pcb->cwnd, pcb->ssthresh));
+
+ if (recv_flags & TF_GOT_FIN) {
+ tcp_ack_now(pcb);
+ pcb->state = CLOSE_WAIT;
+ }
+ } else {
+ /* incorrect ACK number, send RST */
+ tcp_rst(ackno, seqno + tcplen, ip_current_dest_addr(),
+ ip_current_src_addr(), tcphdr->dest, tcphdr->src);
+ }
+ } else if ((flags & TCP_SYN) && (seqno == pcb->rcv_nxt - 1)) {
+ /* Looks like another copy of the SYN - retransmit our SYN-ACK */
+ tcp_rexmit(pcb);
+ }
+ break;
+ case CLOSE_WAIT:
+ /* FALLTHROUGH */
+ case ESTABLISHED:
+ tcp_receive(pcb);
+ if (recv_flags & TF_GOT_FIN) { /* passive close */
+ tcp_ack_now(pcb);
+ pcb->state = CLOSE_WAIT;
+ }
+ break;
+ case FIN_WAIT_1:
+ tcp_receive(pcb);
+ if (recv_flags & TF_GOT_FIN) {
+ if ((flags & TCP_ACK) && (ackno == pcb->snd_nxt) &&
+ pcb->unsent == NULL) {
+ LWIP_DEBUGF(TCP_DEBUG,
+ ("TCP connection closed: FIN_WAIT_1 %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
+ tcp_ack_now(pcb);
+ tcp_pcb_purge(pcb);
+ TCP_RMV_ACTIVE(pcb);
+ pcb->state = TIME_WAIT;
+ TCP_REG(&tcp_tw_pcbs, pcb);
+ } else {
+ tcp_ack_now(pcb);
+ pcb->state = CLOSING;
+ }
+ } else if ((flags & TCP_ACK) && (ackno == pcb->snd_nxt) &&
+ pcb->unsent == NULL) {
+ pcb->state = FIN_WAIT_2;
+ }
+ break;
+ case FIN_WAIT_2:
+ tcp_receive(pcb);
+ if (recv_flags & TF_GOT_FIN) {
+ LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed: FIN_WAIT_2 %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
+ tcp_ack_now(pcb);
+ tcp_pcb_purge(pcb);
+ TCP_RMV_ACTIVE(pcb);
+ pcb->state = TIME_WAIT;
+ TCP_REG(&tcp_tw_pcbs, pcb);
+ }
+ break;
+ case CLOSING:
+ tcp_receive(pcb);
+ if ((flags & TCP_ACK) && ackno == pcb->snd_nxt && pcb->unsent == NULL) {
+ LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed: CLOSING %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
+ tcp_pcb_purge(pcb);
+ TCP_RMV_ACTIVE(pcb);
+ pcb->state = TIME_WAIT;
+ TCP_REG(&tcp_tw_pcbs, pcb);
+ }
+ break;
+ case LAST_ACK:
+ tcp_receive(pcb);
+ if ((flags & TCP_ACK) && ackno == pcb->snd_nxt && pcb->unsent == NULL) {
+ LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed: LAST_ACK %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
+ /* bugfix #21699: don't set pcb->state to CLOSED here or we risk leaking segments */
+ recv_flags |= TF_CLOSED;
+ }
+ break;
+ default:
+ break;
+ }
+ return ERR_OK;
+}
+
+#if TCP_QUEUE_OOSEQ
+/**
+ * Insert segment into the list (segments covered with new one will be deleted)
+ *
+ * Called from tcp_receive()
+ */
+static void
+tcp_oos_insert_segment(struct tcp_seg *cseg, struct tcp_seg *next)
+{
+ struct tcp_seg *old_seg;
+
+ if (TCPH_FLAGS(cseg->tcphdr) & TCP_FIN) {
+ /* received segment overlaps all following segments */
+ tcp_segs_free(next);
+ next = NULL;
+ } else {
+ /* delete some following segments
+ oos queue may have segments with FIN flag */
+ while (next &&
+ TCP_SEQ_GEQ((seqno + cseg->len),
+ (next->tcphdr->seqno + next->len))) {
+ /* cseg with FIN already processed */
+ if (TCPH_FLAGS(next->tcphdr) & TCP_FIN) {
+ TCPH_SET_FLAG(cseg->tcphdr, TCP_FIN);
+ }
+ old_seg = next;
+ next = next->next;
+ tcp_seg_free(old_seg);
+ }
+ if (next &&
+ TCP_SEQ_GT(seqno + cseg->len, next->tcphdr->seqno)) {
+ /* We need to trim the incoming segment. */
+ cseg->len = (u16_t)(next->tcphdr->seqno - seqno);
+ pbuf_realloc(cseg->p, cseg->len);
+ }
+ }
+ cseg->next = next;
+}
+#endif /* TCP_QUEUE_OOSEQ */
+
+/**
+ * Called by tcp_process. Checks if the given segment is an ACK for outstanding
+ * data, and if so frees the memory of the buffered data. Next, it places the
+ * segment on any of the receive queues (pcb->recved or pcb->ooseq). If the segment
+ * is buffered, the pbuf is referenced by pbuf_ref so that it will not be freed until
+ * it has been removed from the buffer.
+ *
+ * If the incoming segment constitutes an ACK for a segment that was used for RTT
+ * estimation, the RTT is estimated here as well.
+ *
+ * Called from tcp_process().
+ */
+static void
+tcp_receive(struct tcp_pcb *pcb)
+{
+ struct tcp_seg *next;
+#if TCP_QUEUE_OOSEQ
+ struct tcp_seg *prev, *cseg;
+#endif /* TCP_QUEUE_OOSEQ */
+ s32_t off;
+ s16_t m;
+ u32_t right_wnd_edge;
+ u16_t new_tot_len;
+ int found_dupack = 0;
+#if TCP_OOSEQ_MAX_BYTES || TCP_OOSEQ_MAX_PBUFS
+ u32_t ooseq_blen;
+ u16_t ooseq_qlen;
+#endif /* TCP_OOSEQ_MAX_BYTES || TCP_OOSEQ_MAX_PBUFS */
+
+ LWIP_ASSERT("tcp_receive: wrong state", pcb->state >= ESTABLISHED);
+
+ if (flags & TCP_ACK) {
+ right_wnd_edge = pcb->snd_wnd + pcb->snd_wl2;
+
+ /* Update window. */
+ if (TCP_SEQ_LT(pcb->snd_wl1, seqno) ||
+ (pcb->snd_wl1 == seqno && TCP_SEQ_LT(pcb->snd_wl2, ackno)) ||
+ (pcb->snd_wl2 == ackno && (u32_t)SND_WND_SCALE(pcb, tcphdr->wnd) > pcb->snd_wnd)) {
+ pcb->snd_wnd = SND_WND_SCALE(pcb, tcphdr->wnd);
+ /* keep track of the biggest window announced by the remote host to calculate
+ the maximum segment size */
+ if (pcb->snd_wnd_max < pcb->snd_wnd) {
+ pcb->snd_wnd_max = pcb->snd_wnd;
+ }
+ pcb->snd_wl1 = seqno;
+ pcb->snd_wl2 = ackno;
+ if (pcb->snd_wnd == 0) {
+ if (pcb->persist_backoff == 0) {
+ /* start persist timer */
+ pcb->persist_cnt = 0;
+ pcb->persist_backoff = 1;
+ }
+ } else if (pcb->persist_backoff > 0) {
+ /* stop persist timer */
+ pcb->persist_backoff = 0;
+ }
+ LWIP_DEBUGF(TCP_WND_DEBUG, ("tcp_receive: window update %"TCPWNDSIZE_F"\n", pcb->snd_wnd));
+#if TCP_WND_DEBUG
+ } else {
+ if (pcb->snd_wnd != (tcpwnd_size_t)SND_WND_SCALE(pcb, tcphdr->wnd)) {
+ LWIP_DEBUGF(TCP_WND_DEBUG,
+ ("tcp_receive: no window update lastack %"U32_F" ackno %"
+ U32_F" wl1 %"U32_F" seqno %"U32_F" wl2 %"U32_F"\n",
+ pcb->lastack, ackno, pcb->snd_wl1, seqno, pcb->snd_wl2));
+ }
+#endif /* TCP_WND_DEBUG */
+ }
+
+ /* (From Stevens TCP/IP Illustrated Vol II, p970.) Its only a
+ * duplicate ack if:
+ * 1) It doesn't ACK new data
+ * 2) length of received packet is zero (i.e. no payload)
+ * 3) the advertised window hasn't changed
+ * 4) There is outstanding unacknowledged data (retransmission timer running)
+ * 5) The ACK is == biggest ACK sequence number so far seen (snd_una)
+ *
+ * If it passes all five, should process as a dupack:
+ * a) dupacks < 3: do nothing
+ * b) dupacks == 3: fast retransmit
+ * c) dupacks > 3: increase cwnd
+ *
+ * If it only passes 1-3, should reset dupack counter (and add to
+ * stats, which we don't do in lwIP)
+ *
+ * If it only passes 1, should reset dupack counter
+ *
+ */
+
+ /* Clause 1 */
+ if (TCP_SEQ_LEQ(ackno, pcb->lastack)) {
+ /* Clause 2 */
+ if (tcplen == 0) {
+ /* Clause 3 */
+ if (pcb->snd_wl2 + pcb->snd_wnd == right_wnd_edge) {
+ /* Clause 4 */
+ if (pcb->rtime >= 0) {
+ /* Clause 5 */
+ if (pcb->lastack == ackno) {
+ found_dupack = 1;
+ if ((u8_t)(pcb->dupacks + 1) > pcb->dupacks) {
+ ++pcb->dupacks;
+ }
+ if (pcb->dupacks > 3) {
+ /* Inflate the congestion window, but not if it means that
+ the value overflows. */
+ if ((tcpwnd_size_t)(pcb->cwnd + pcb->mss) > pcb->cwnd) {
+ pcb->cwnd += pcb->mss;
+ }
+ } else if (pcb->dupacks == 3) {
+ /* Do fast retransmit */
+ tcp_rexmit_fast(pcb);
+ }
+ }
+ }
+ }
+ }
+ /* If Clause (1) or more is true, but not a duplicate ack, reset
+ * count of consecutive duplicate acks */
+ if (!found_dupack) {
+ pcb->dupacks = 0;
+ }
+ } else if (TCP_SEQ_BETWEEN(ackno, pcb->lastack+1, pcb->snd_nxt)) {
+ /* We come here when the ACK acknowledges new data. */
+
+ /* Reset the "IN Fast Retransmit" flag, since we are no longer
+ in fast retransmit. Also reset the congestion window to the
+ slow start threshold. */
+ if (pcb->flags & TF_INFR) {
+ pcb->flags &= ~TF_INFR;
+ pcb->cwnd = pcb->ssthresh;
+ }
+
+ /* Reset the number of retransmissions. */
+ pcb->nrtx = 0;
+
+ /* Reset the retransmission time-out. */
+ pcb->rto = (pcb->sa >> 3) + pcb->sv;
+
+ /* Reset the fast retransmit variables. */
+ pcb->dupacks = 0;
+ pcb->lastack = ackno;
+
+ /* Update the congestion control variables (cwnd and
+ ssthresh). */
+ if (pcb->state >= ESTABLISHED) {
+ if (pcb->cwnd < pcb->ssthresh) {
+ if ((tcpwnd_size_t)(pcb->cwnd + pcb->mss) > pcb->cwnd) {
+ pcb->cwnd += pcb->mss;
+ }
+ LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_receive: slow start cwnd %"TCPWNDSIZE_F"\n", pcb->cwnd));
+ } else {
+ tcpwnd_size_t new_cwnd = (pcb->cwnd + pcb->mss * pcb->mss / pcb->cwnd);
+ if (new_cwnd > pcb->cwnd) {
+ pcb->cwnd = new_cwnd;
+ }
+ LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_receive: congestion avoidance cwnd %"TCPWNDSIZE_F"\n", pcb->cwnd));
+ }
+ }
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: ACK for %"U32_F", unacked->seqno %"U32_F":%"U32_F"\n",
+ ackno,
+ pcb->unacked != NULL?
+ lwip_ntohl(pcb->unacked->tcphdr->seqno): 0,
+ pcb->unacked != NULL?
+ lwip_ntohl(pcb->unacked->tcphdr->seqno) + TCP_TCPLEN(pcb->unacked): 0));
+
+ /* Remove segment from the unacknowledged list if the incoming
+ ACK acknowledges them. */
+ while (pcb->unacked != NULL &&
+ TCP_SEQ_LEQ(lwip_ntohl(pcb->unacked->tcphdr->seqno) +
+ TCP_TCPLEN(pcb->unacked), ackno)) {
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: removing %"U32_F":%"U32_F" from pcb->unacked\n",
+ lwip_ntohl(pcb->unacked->tcphdr->seqno),
+ lwip_ntohl(pcb->unacked->tcphdr->seqno) +
+ TCP_TCPLEN(pcb->unacked)));
+
+ next = pcb->unacked;
+ pcb->unacked = pcb->unacked->next;
+
+ LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_receive: queuelen %"TCPWNDSIZE_F" ... ", (tcpwnd_size_t)pcb->snd_queuelen));
+ LWIP_ASSERT("pcb->snd_queuelen >= pbuf_clen(next->p)", (pcb->snd_queuelen >= pbuf_clen(next->p)));
+
+ pcb->snd_queuelen -= pbuf_clen(next->p);
+ recv_acked += next->len;
+ tcp_seg_free(next);
+
+ LWIP_DEBUGF(TCP_QLEN_DEBUG, ("%"TCPWNDSIZE_F" (after freeing unacked)\n", (tcpwnd_size_t)pcb->snd_queuelen));
+ if (pcb->snd_queuelen != 0) {
+ LWIP_ASSERT("tcp_receive: valid queue length", pcb->unacked != NULL ||
+ pcb->unsent != NULL);
+ }
+ }
+
+ /* If there's nothing left to acknowledge, stop the retransmit
+ timer, otherwise reset it to start again */
+ if (pcb->unacked == NULL) {
+ pcb->rtime = -1;
+ } else {
+ pcb->rtime = 0;
+ }
+
+ pcb->polltmr = 0;
+
+#if LWIP_IPV6 && LWIP_ND6_TCP_REACHABILITY_HINTS
+ if (ip_current_is_v6()) {
+ /* Inform neighbor reachability of forward progress. */
+ nd6_reachability_hint(ip6_current_src_addr());
+ }
+#endif /* LWIP_IPV6 && LWIP_ND6_TCP_REACHABILITY_HINTS*/
+ } else {
+ /* Out of sequence ACK, didn't really ack anything */
+ tcp_send_empty_ack(pcb);
+ }
+
+ /* We go through the ->unsent list to see if any of the segments
+ on the list are acknowledged by the ACK. This may seem
+ strange since an "unsent" segment shouldn't be acked. The
+ rationale is that lwIP puts all outstanding segments on the
+ ->unsent list after a retransmission, so these segments may
+ in fact have been sent once. */
+ while (pcb->unsent != NULL &&
+ TCP_SEQ_BETWEEN(ackno, lwip_ntohl(pcb->unsent->tcphdr->seqno) +
+ TCP_TCPLEN(pcb->unsent), pcb->snd_nxt)) {
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: removing %"U32_F":%"U32_F" from pcb->unsent\n",
+ lwip_ntohl(pcb->unsent->tcphdr->seqno), lwip_ntohl(pcb->unsent->tcphdr->seqno) +
+ TCP_TCPLEN(pcb->unsent)));
+
+ next = pcb->unsent;
+ pcb->unsent = pcb->unsent->next;
+#if TCP_OVERSIZE
+ if (pcb->unsent == NULL) {
+ pcb->unsent_oversize = 0;
+ }
+#endif /* TCP_OVERSIZE */
+ LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_receive: queuelen %"TCPWNDSIZE_F" ... ", (tcpwnd_size_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 */
+ pcb->snd_queuelen -= pbuf_clen(next->p);
+ recv_acked += next->len;
+ tcp_seg_free(next);
+ LWIP_DEBUGF(TCP_QLEN_DEBUG, ("%"TCPWNDSIZE_F" (after freeing unsent)\n", (tcpwnd_size_t)pcb->snd_queuelen));
+ if (pcb->snd_queuelen != 0) {
+ LWIP_ASSERT("tcp_receive: valid queue length",
+ pcb->unacked != NULL || pcb->unsent != NULL);
+ }
+ }
+ pcb->snd_buf += recv_acked;
+ /* End of ACK for new data processing. */
+
+ LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_receive: pcb->rttest %"U32_F" rtseq %"U32_F" ackno %"U32_F"\n",
+ pcb->rttest, pcb->rtseq, ackno));
+
+ /* RTT estimation calculations. This is done by checking if the
+ incoming segment acknowledges the segment we use to take a
+ round-trip time measurement. */
+ if (pcb->rttest && TCP_SEQ_LT(pcb->rtseq, ackno)) {
+ /* diff between this shouldn't exceed 32K since this are tcp timer ticks
+ and a round-trip shouldn't be that long... */
+ m = (s16_t)(tcp_ticks - pcb->rttest);
+
+ LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_receive: experienced rtt %"U16_F" ticks (%"U16_F" msec).\n",
+ m, (u16_t)(m * TCP_SLOW_INTERVAL)));
+
+ /* This is taken directly from VJs original code in his paper */
+ m = m - (pcb->sa >> 3);
+ pcb->sa += m;
+ if (m < 0) {
+ m = -m;
+ }
+ m = m - (pcb->sv >> 2);
+ pcb->sv += m;
+ pcb->rto = (pcb->sa >> 3) + pcb->sv;
+
+ LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_receive: RTO %"U16_F" (%"U16_F" milliseconds)\n",
+ pcb->rto, (u16_t)(pcb->rto * TCP_SLOW_INTERVAL)));
+
+ pcb->rttest = 0;
+ }
+ }
+
+ /* If the incoming segment contains data, we must process it
+ further unless the pcb already received a FIN.
+ (RFC 793, chapter 3.9, "SEGMENT ARRIVES" in states CLOSE-WAIT, CLOSING,
+ LAST-ACK and TIME-WAIT: "Ignore the segment text.") */
+ if ((tcplen > 0) && (pcb->state < CLOSE_WAIT)) {
+ /* This code basically does three things:
+
+ +) If the incoming segment contains data that is the next
+ in-sequence data, this data is passed to the application. This
+ might involve trimming the first edge of the data. The rcv_nxt
+ variable and the advertised window are adjusted.
+
+ +) If the incoming segment has data that is above the next
+ sequence number expected (->rcv_nxt), the segment is placed on
+ the ->ooseq queue. This is done by finding the appropriate
+ place in the ->ooseq queue (which is ordered by sequence
+ number) and trim the segment in both ends if needed. An
+ immediate ACK is sent to indicate that we received an
+ out-of-sequence segment.
+
+ +) Finally, we check if the first segment on the ->ooseq queue
+ now is in sequence (i.e., if rcv_nxt >= ooseq->seqno). If
+ rcv_nxt > ooseq->seqno, we must trim the first edge of the
+ segment on ->ooseq before we adjust rcv_nxt. The data in the
+ segments that are now on sequence are chained onto the
+ incoming segment so that we only need to call the application
+ once.
+ */
+
+ /* First, we check if we must trim the first edge. We have to do
+ this if the sequence number of the incoming segment is less
+ than rcv_nxt, and the sequence number plus the length of the
+ segment is larger than rcv_nxt. */
+ /* if (TCP_SEQ_LT(seqno, pcb->rcv_nxt)) {
+ if (TCP_SEQ_LT(pcb->rcv_nxt, seqno + tcplen)) {*/
+ if (TCP_SEQ_BETWEEN(pcb->rcv_nxt, seqno + 1, seqno + tcplen - 1)) {
+ /* Trimming the first edge is done by pushing the payload
+ pointer in the pbuf downwards. This is somewhat tricky since
+ we do not want to discard the full contents of the pbuf up to
+ the new starting point of the data since we have to keep the
+ TCP header which is present in the first pbuf in the chain.
+
+ What is done is really quite a nasty hack: the first pbuf in
+ the pbuf chain is pointed to by inseg.p. Since we need to be
+ able to deallocate the whole pbuf, we cannot change this
+ inseg.p pointer to point to any of the later pbufs in the
+ chain. Instead, we point the ->payload pointer in the first
+ pbuf to data in one of the later pbufs. We also set the
+ inseg.data pointer to point to the right place. This way, the
+ ->p pointer will still point to the first pbuf, but the
+ ->p->payload pointer will point to data in another pbuf.
+
+ After we are done with adjusting the pbuf pointers we must
+ adjust the ->data pointer in the seg and the segment
+ length.*/
+
+ struct pbuf *p = inseg.p;
+ off = pcb->rcv_nxt - seqno;
+ LWIP_ASSERT("inseg.p != NULL", inseg.p);
+ LWIP_ASSERT("insane offset!", (off < 0x7fff));
+ if (inseg.p->len < off) {
+ LWIP_ASSERT("pbuf too short!", (((s32_t)inseg.p->tot_len) >= off));
+ new_tot_len = (u16_t)(inseg.p->tot_len - off);
+ while (p->len < off) {
+ off -= p->len;
+ /* KJM following line changed (with addition of new_tot_len var)
+ to fix bug #9076
+ inseg.p->tot_len -= p->len; */
+ p->tot_len = new_tot_len;
+ p->len = 0;
+ p = p->next;
+ }
+ if (pbuf_header(p, (s16_t)-off)) {
+ /* Do we need to cope with this failing? Assert for now */
+ LWIP_ASSERT("pbuf_header failed", 0);
+ }
+ } else {
+ if (pbuf_header(inseg.p, (s16_t)-off)) {
+ /* Do we need to cope with this failing? Assert for now */
+ LWIP_ASSERT("pbuf_header failed", 0);
+ }
+ }
+ inseg.len -= (u16_t)(pcb->rcv_nxt - seqno);
+ inseg.tcphdr->seqno = seqno = pcb->rcv_nxt;
+ }
+ else {
+ if (TCP_SEQ_LT(seqno, pcb->rcv_nxt)) {
+ /* the whole segment is < rcv_nxt */
+ /* must be a duplicate of a packet that has already been correctly handled */
+
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: duplicate seqno %"U32_F"\n", seqno));
+ tcp_ack_now(pcb);
+ }
+ }
+
+ /* The sequence number must be within the window (above rcv_nxt
+ and below rcv_nxt + rcv_wnd) in order to be further
+ processed. */
+ if (TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt,
+ pcb->rcv_nxt + pcb->rcv_wnd - 1)) {
+ if (pcb->rcv_nxt == seqno) {
+ /* The incoming segment is the next in sequence. We check if
+ we have to trim the end of the segment and update rcv_nxt
+ and pass the data to the application. */
+ tcplen = TCP_TCPLEN(&inseg);
+
+ if (tcplen > pcb->rcv_wnd) {
+ LWIP_DEBUGF(TCP_INPUT_DEBUG,
+ ("tcp_receive: other end overran receive window"
+ "seqno %"U32_F" len %"U16_F" right edge %"U32_F"\n",
+ seqno, tcplen, pcb->rcv_nxt + pcb->rcv_wnd));
+ if (TCPH_FLAGS(inseg.tcphdr) & TCP_FIN) {
+ /* Must remove the FIN from the header as we're trimming
+ * that byte of sequence-space from the packet */
+ TCPH_FLAGS_SET(inseg.tcphdr, TCPH_FLAGS(inseg.tcphdr) & ~(unsigned int)TCP_FIN);
+ }
+ /* Adjust length of segment to fit in the window. */
+ TCPWND_CHECK16(pcb->rcv_wnd);
+ inseg.len = (u16_t)pcb->rcv_wnd;
+ if (TCPH_FLAGS(inseg.tcphdr) & TCP_SYN) {
+ inseg.len -= 1;
+ }
+ pbuf_realloc(inseg.p, inseg.len);
+ tcplen = TCP_TCPLEN(&inseg);
+ LWIP_ASSERT("tcp_receive: segment not trimmed correctly to rcv_wnd\n",
+ (seqno + tcplen) == (pcb->rcv_nxt + pcb->rcv_wnd));
+ }
+#if TCP_QUEUE_OOSEQ
+ /* Received in-sequence data, adjust ooseq data if:
+ - FIN has been received or
+ - inseq overlaps with ooseq */
+ if (pcb->ooseq != NULL) {
+ if (TCPH_FLAGS(inseg.tcphdr) & TCP_FIN) {
+ LWIP_DEBUGF(TCP_INPUT_DEBUG,
+ ("tcp_receive: received in-order FIN, binning ooseq queue\n"));
+ /* Received in-order FIN means anything that was received
+ * out of order must now have been received in-order, so
+ * bin the ooseq queue */
+ while (pcb->ooseq != NULL) {
+ struct tcp_seg *old_ooseq = pcb->ooseq;
+ pcb->ooseq = pcb->ooseq->next;
+ tcp_seg_free(old_ooseq);
+ }
+ } else {
+ next = pcb->ooseq;
+ /* Remove all segments on ooseq that are covered by inseg already.
+ * FIN is copied from ooseq to inseg if present. */
+ while (next &&
+ TCP_SEQ_GEQ(seqno + tcplen,
+ next->tcphdr->seqno + next->len)) {
+ /* inseg cannot have FIN here (already processed above) */
+ if ((TCPH_FLAGS(next->tcphdr) & TCP_FIN) != 0 &&
+ (TCPH_FLAGS(inseg.tcphdr) & TCP_SYN) == 0) {
+ TCPH_SET_FLAG(inseg.tcphdr, TCP_FIN);
+ tcplen = TCP_TCPLEN(&inseg);
+ }
+ prev = next;
+ next = next->next;
+ tcp_seg_free(prev);
+ }
+ /* Now trim right side of inseg if it overlaps with the first
+ * segment on ooseq */
+ if (next &&
+ TCP_SEQ_GT(seqno + tcplen,
+ next->tcphdr->seqno)) {
+ /* inseg cannot have FIN here (already processed above) */
+ inseg.len = (u16_t)(next->tcphdr->seqno - seqno);
+ if (TCPH_FLAGS(inseg.tcphdr) & TCP_SYN) {
+ inseg.len -= 1;
+ }
+ pbuf_realloc(inseg.p, inseg.len);
+ tcplen = TCP_TCPLEN(&inseg);
+ LWIP_ASSERT("tcp_receive: segment not trimmed correctly to ooseq queue\n",
+ (seqno + tcplen) == next->tcphdr->seqno);
+ }
+ pcb->ooseq = next;
+ }
+ }
+#endif /* TCP_QUEUE_OOSEQ */
+
+ pcb->rcv_nxt = seqno + tcplen;
+
+ /* Update the receiver's (our) window. */
+ LWIP_ASSERT("tcp_receive: tcplen > rcv_wnd\n", pcb->rcv_wnd >= tcplen);
+ pcb->rcv_wnd -= tcplen;
+
+ tcp_update_rcv_ann_wnd(pcb);
+
+ /* If there is data in the segment, we make preparations to
+ pass this up to the application. The ->recv_data variable
+ is used for holding the pbuf that goes to the
+ application. The code for reassembling out-of-sequence data
+ chains its data on this pbuf as well.
+
+ If the segment was a FIN, we set the TF_GOT_FIN flag that will
+ be used to indicate to the application that the remote side has
+ closed its end of the connection. */
+ if (inseg.p->tot_len > 0) {
+ recv_data = inseg.p;
+ /* Since this pbuf now is the responsibility of the
+ application, we delete our reference to it so that we won't
+ (mistakingly) deallocate it. */
+ inseg.p = NULL;
+ }
+ if (TCPH_FLAGS(inseg.tcphdr) & TCP_FIN) {
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: received FIN.\n"));
+ recv_flags |= TF_GOT_FIN;
+ }
+
+#if TCP_QUEUE_OOSEQ
+ /* We now check if we have segments on the ->ooseq queue that
+ are now in sequence. */
+ while (pcb->ooseq != NULL &&
+ pcb->ooseq->tcphdr->seqno == pcb->rcv_nxt) {
+
+ cseg = pcb->ooseq;
+ seqno = pcb->ooseq->tcphdr->seqno;
+
+ pcb->rcv_nxt += TCP_TCPLEN(cseg);
+ LWIP_ASSERT("tcp_receive: ooseq tcplen > rcv_wnd\n",
+ pcb->rcv_wnd >= TCP_TCPLEN(cseg));
+ pcb->rcv_wnd -= TCP_TCPLEN(cseg);
+
+ tcp_update_rcv_ann_wnd(pcb);
+
+ if (cseg->p->tot_len > 0) {
+ /* Chain this pbuf onto the pbuf that we will pass to
+ the application. */
+ /* With window scaling, this can overflow recv_data->tot_len, but
+ that's not a problem since we explicitly fix that before passing
+ recv_data to the application. */
+ if (recv_data) {
+ pbuf_cat(recv_data, cseg->p);
+ } else {
+ recv_data = cseg->p;
+ }
+ cseg->p = NULL;
+ }
+ if (TCPH_FLAGS(cseg->tcphdr) & TCP_FIN) {
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: dequeued FIN.\n"));
+ recv_flags |= TF_GOT_FIN;
+ if (pcb->state == ESTABLISHED) { /* force passive close or we can move to active close */
+ pcb->state = CLOSE_WAIT;
+ }
+ }
+
+ pcb->ooseq = cseg->next;
+ tcp_seg_free(cseg);
+ }
+#endif /* TCP_QUEUE_OOSEQ */
+
+
+ /* Acknowledge the segment(s). */
+ tcp_ack(pcb);
+
+#if LWIP_IPV6 && LWIP_ND6_TCP_REACHABILITY_HINTS
+ if (ip_current_is_v6()) {
+ /* Inform neighbor reachability of forward progress. */
+ nd6_reachability_hint(ip6_current_src_addr());
+ }
+#endif /* LWIP_IPV6 && LWIP_ND6_TCP_REACHABILITY_HINTS*/
+
+ } else {
+ /* We get here if the incoming segment is out-of-sequence. */
+ tcp_send_empty_ack(pcb);
+#if TCP_QUEUE_OOSEQ
+ /* We queue the segment on the ->ooseq queue. */
+ if (pcb->ooseq == NULL) {
+ pcb->ooseq = tcp_seg_copy(&inseg);
+ } else {
+ /* If the queue is not empty, we walk through the queue and
+ try to find a place where the sequence number of the
+ incoming segment is between the sequence numbers of the
+ previous and the next segment on the ->ooseq queue. That is
+ the place where we put the incoming segment. If needed, we
+ trim the second edges of the previous and the incoming
+ segment so that it will fit into the sequence.
+
+ If the incoming segment has the same sequence number as a
+ segment on the ->ooseq queue, we discard the segment that
+ contains less data. */
+
+ prev = NULL;
+ for (next = pcb->ooseq; next != NULL; next = next->next) {
+ if (seqno == next->tcphdr->seqno) {
+ /* The sequence number of the incoming segment is the
+ same as the sequence number of the segment on
+ ->ooseq. We check the lengths to see which one to
+ discard. */
+ if (inseg.len > next->len) {
+ /* The incoming segment is larger than the old
+ segment. We replace some segments with the new
+ one. */
+ cseg = tcp_seg_copy(&inseg);
+ if (cseg != NULL) {
+ if (prev != NULL) {
+ prev->next = cseg;
+ } else {
+ pcb->ooseq = cseg;
+ }
+ tcp_oos_insert_segment(cseg, next);
+ }
+ break;
+ } else {
+ /* Either the lengths are the same or the incoming
+ segment was smaller than the old one; in either
+ case, we ditch the incoming segment. */
+ break;
+ }
+ } else {
+ if (prev == NULL) {
+ if (TCP_SEQ_LT(seqno, next->tcphdr->seqno)) {
+ /* The sequence number of the incoming segment is lower
+ than the sequence number of the first segment on the
+ queue. We put the incoming segment first on the
+ queue. */
+ cseg = tcp_seg_copy(&inseg);
+ if (cseg != NULL) {
+ pcb->ooseq = cseg;
+ tcp_oos_insert_segment(cseg, next);
+ }
+ break;
+ }
+ } else {
+ /*if (TCP_SEQ_LT(prev->tcphdr->seqno, seqno) &&
+ TCP_SEQ_LT(seqno, next->tcphdr->seqno)) {*/
+ if (TCP_SEQ_BETWEEN(seqno, prev->tcphdr->seqno+1, next->tcphdr->seqno-1)) {
+ /* The sequence number of the incoming segment is in
+ between the sequence numbers of the previous and
+ the next segment on ->ooseq. We trim trim the previous
+ segment, delete next segments that included in received segment
+ and trim received, if needed. */
+ cseg = tcp_seg_copy(&inseg);
+ if (cseg != NULL) {
+ if (TCP_SEQ_GT(prev->tcphdr->seqno + prev->len, seqno)) {
+ /* We need to trim the prev segment. */
+ prev->len = (u16_t)(seqno - prev->tcphdr->seqno);
+ pbuf_realloc(prev->p, prev->len);
+ }
+ prev->next = cseg;
+ tcp_oos_insert_segment(cseg, next);
+ }
+ break;
+ }
+ }
+ /* If the "next" segment is the last segment on the
+ ooseq queue, we add the incoming segment to the end
+ of the list. */
+ if (next->next == NULL &&
+ TCP_SEQ_GT(seqno, next->tcphdr->seqno)) {
+ if (TCPH_FLAGS(next->tcphdr) & TCP_FIN) {
+ /* segment "next" already contains all data */
+ break;
+ }
+ next->next = tcp_seg_copy(&inseg);
+ if (next->next != NULL) {
+ if (TCP_SEQ_GT(next->tcphdr->seqno + next->len, seqno)) {
+ /* We need to trim the last segment. */
+ next->len = (u16_t)(seqno - next->tcphdr->seqno);
+ pbuf_realloc(next->p, next->len);
+ }
+ /* check if the remote side overruns our receive window */
+ if (TCP_SEQ_GT((u32_t)tcplen + seqno, pcb->rcv_nxt + (u32_t)pcb->rcv_wnd)) {
+ LWIP_DEBUGF(TCP_INPUT_DEBUG,
+ ("tcp_receive: other end overran receive window"
+ "seqno %"U32_F" len %"U16_F" right edge %"U32_F"\n",
+ seqno, tcplen, pcb->rcv_nxt + pcb->rcv_wnd));
+ if (TCPH_FLAGS(next->next->tcphdr) & TCP_FIN) {
+ /* Must remove the FIN from the header as we're trimming
+ * that byte of sequence-space from the packet */
+ TCPH_FLAGS_SET(next->next->tcphdr, TCPH_FLAGS(next->next->tcphdr) & ~TCP_FIN);
+ }
+ /* Adjust length of segment to fit in the window. */
+ next->next->len = (u16_t)(pcb->rcv_nxt + pcb->rcv_wnd - seqno);
+ pbuf_realloc(next->next->p, next->next->len);
+ tcplen = TCP_TCPLEN(next->next);
+ LWIP_ASSERT("tcp_receive: segment not trimmed correctly to rcv_wnd\n",
+ (seqno + tcplen) == (pcb->rcv_nxt + pcb->rcv_wnd));
+ }
+ }
+ break;
+ }
+ }
+ prev = next;
+ }
+ }
+#if TCP_OOSEQ_MAX_BYTES || TCP_OOSEQ_MAX_PBUFS
+ /* Check that the data on ooseq doesn't exceed one of the limits
+ and throw away everything above that limit. */
+ ooseq_blen = 0;
+ ooseq_qlen = 0;
+ prev = NULL;
+ for (next = pcb->ooseq; next != NULL; prev = next, next = next->next) {
+ struct pbuf *p = next->p;
+ ooseq_blen += p->tot_len;
+ ooseq_qlen += pbuf_clen(p);
+ if ((ooseq_blen > TCP_OOSEQ_MAX_BYTES) ||
+ (ooseq_qlen > TCP_OOSEQ_MAX_PBUFS)) {
+ /* too much ooseq data, dump this and everything after it */
+ tcp_segs_free(next);
+ if (prev == NULL) {
+ /* first ooseq segment is too much, dump the whole queue */
+ pcb->ooseq = NULL;
+ } else {
+ /* just dump 'next' and everything after it */
+ prev->next = NULL;
+ }
+ break;
+ }
+ }
+#endif /* TCP_OOSEQ_MAX_BYTES || TCP_OOSEQ_MAX_PBUFS */
+#endif /* TCP_QUEUE_OOSEQ */
+ }
+ } else {
+ /* The incoming segment is not within the window. */
+ tcp_send_empty_ack(pcb);
+ }
+ } else {
+ /* Segments with length 0 is taken care of here. Segments that
+ fall out of the window are ACKed. */
+ if (!TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, pcb->rcv_nxt + pcb->rcv_wnd - 1)) {
+ tcp_ack_now(pcb);
+ }
+ }
+}
+
+static u8_t
+tcp_getoptbyte(void)
+{
+ if ((tcphdr_opt2 == NULL) || (tcp_optidx < tcphdr_opt1len)) {
+ u8_t* opts = (u8_t *)tcphdr + TCP_HLEN;
+ return opts[tcp_optidx++];
+ } else {
+ u8_t idx = (u8_t)(tcp_optidx++ - tcphdr_opt1len);
+ return tcphdr_opt2[idx];
+ }
+}
+
+/**
+ * Parses the options contained in the incoming segment.
+ *
+ * Called from tcp_listen_input() and tcp_process().
+ * Currently, only the MSS option is supported!
+ *
+ * @param pcb the tcp_pcb for which a segment arrived
+ */
+static void
+tcp_parseopt(struct tcp_pcb *pcb)
+{
+ u8_t data;
+ u16_t mss;
+#if LWIP_TCP_TIMESTAMPS
+ u32_t tsval;
+#endif
+
+ /* Parse the TCP MSS option, if present. */
+ if (tcphdr_optlen != 0) {
+ for (tcp_optidx = 0; tcp_optidx < tcphdr_optlen; ) {
+ u8_t opt = tcp_getoptbyte();
+ switch (opt) {
+ case LWIP_TCP_OPT_EOL:
+ /* End of options. */
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: EOL\n"));
+ return;
+ case LWIP_TCP_OPT_NOP:
+ /* NOP option. */
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: NOP\n"));
+ break;
+ case LWIP_TCP_OPT_MSS:
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: MSS\n"));
+ if (tcp_getoptbyte() != LWIP_TCP_OPT_LEN_MSS || (tcp_optidx - 2 + LWIP_TCP_OPT_LEN_MSS) > tcphdr_optlen) {
+ /* Bad length */
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: bad length\n"));
+ return;
+ }
+ /* An MSS option with the right option length. */
+ mss = (tcp_getoptbyte() << 8);
+ mss |= tcp_getoptbyte();
+ /* Limit the mss to the configured TCP_MSS and prevent division by zero */
+ pcb->mss = ((mss > TCP_MSS) || (mss == 0)) ? TCP_MSS : mss;
+ break;
+#if LWIP_WND_SCALE
+ case LWIP_TCP_OPT_WS:
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: WND_SCALE\n"));
+ if (tcp_getoptbyte() != LWIP_TCP_OPT_LEN_WS || (tcp_optidx - 2 + LWIP_TCP_OPT_LEN_WS) > tcphdr_optlen) {
+ /* Bad length */
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: bad length\n"));
+ return;
+ }
+ /* If syn was received with wnd scale option,
+ activate wnd scale opt, but only if this is not a retransmission */
+ if ((flags & TCP_SYN) && !(pcb->flags & TF_WND_SCALE)) {
+ /* An WND_SCALE option with the right option length. */
+ data = tcp_getoptbyte();
+ pcb->snd_scale = data;
+ if (pcb->snd_scale > 14U) {
+ pcb->snd_scale = 14U;
+ }
+ pcb->rcv_scale = TCP_RCV_SCALE;
+ pcb->flags |= TF_WND_SCALE;
+ /* window scaling is enabled, we can use the full receive window */
+ LWIP_ASSERT("window not at default value", pcb->rcv_wnd == TCPWND_MIN16(TCP_WND));
+ LWIP_ASSERT("window not at default value", pcb->rcv_ann_wnd == TCPWND_MIN16(TCP_WND));
+ pcb->rcv_wnd = pcb->rcv_ann_wnd = TCP_WND;
+ }
+ break;
+#endif
+#if LWIP_TCP_TIMESTAMPS
+ case LWIP_TCP_OPT_TS:
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: TS\n"));
+ if (tcp_getoptbyte() != LWIP_TCP_OPT_LEN_TS || (tcp_optidx - 2 + LWIP_TCP_OPT_LEN_TS) > tcphdr_optlen) {
+ /* Bad length */
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: bad length\n"));
+ return;
+ }
+ /* TCP timestamp option with valid length */
+ tsval = tcp_getoptbyte();
+ tsval |= (tcp_getoptbyte() << 8);
+ tsval |= (tcp_getoptbyte() << 16);
+ tsval |= (tcp_getoptbyte() << 24);
+ if (flags & TCP_SYN) {
+ pcb->ts_recent = lwip_ntohl(tsval);
+ /* Enable sending timestamps in every segment now that we know
+ the remote host supports it. */
+ pcb->flags |= TF_TIMESTAMP;
+ } else if (TCP_SEQ_BETWEEN(pcb->ts_lastacksent, seqno, seqno+tcplen)) {
+ pcb->ts_recent = lwip_ntohl(tsval);
+ }
+ /* Advance to next option (6 bytes already read) */
+ tcp_optidx += LWIP_TCP_OPT_LEN_TS - 6;
+ break;
+#endif
+ default:
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: other\n"));
+ data = tcp_getoptbyte();
+ if (data < 2) {
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: bad length\n"));
+ /* If the length field is zero, the options are malformed
+ and we don't process them further. */
+ return;
+ }
+ /* All other options have a length field, so that we easily
+ can skip past them. */
+ tcp_optidx += data - 2;
+ }
+ }
+ }
+}
+
+void
+tcp_trigger_input_pcb_close(void)
+{
+ recv_flags |= TF_CLOSED;
+}
+
+#endif /* LWIP_TCP */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/tcp_out.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/tcp_out.c
new file mode 100644
index 0000000..2435408
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/tcp_out.c
@@ -0,0 +1,1671 @@
+/**
+ * @file
+ * Transmission Control Protocol, outgoing traffic
+ *
+ * The output functions of TCP.
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_TCP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/priv/tcp_priv.h"
+#include "lwip/def.h"
+#include "lwip/mem.h"
+#include "lwip/memp.h"
+#include "lwip/ip_addr.h"
+#include "lwip/netif.h"
+#include "lwip/inet_chksum.h"
+#include "lwip/stats.h"
+#include "lwip/ip6.h"
+#include "lwip/ip6_addr.h"
+#if LWIP_TCP_TIMESTAMPS
+#include "lwip/sys.h"
+#endif
+
+#include <string.h>
+
+/* Define some copy-macros for checksum-on-copy so that the code looks
+ nicer by preventing too many ifdef's. */
+#if TCP_CHECKSUM_ON_COPY
+#define TCP_DATA_COPY(dst, src, len, seg) do { \
+ tcp_seg_add_chksum(LWIP_CHKSUM_COPY(dst, src, len), \
+ len, &seg->chksum, &seg->chksum_swapped); \
+ seg->flags |= TF_SEG_DATA_CHECKSUMMED; } while(0)
+#define TCP_DATA_COPY2(dst, src, len, chksum, chksum_swapped) \
+ tcp_seg_add_chksum(LWIP_CHKSUM_COPY(dst, src, len), len, chksum, chksum_swapped);
+#else /* TCP_CHECKSUM_ON_COPY*/
+#define TCP_DATA_COPY(dst, src, len, seg) MEMCPY(dst, src, len)
+#define TCP_DATA_COPY2(dst, src, len, chksum, chksum_swapped) MEMCPY(dst, src, len)
+#endif /* TCP_CHECKSUM_ON_COPY*/
+
+/** Define this to 1 for an extra check that the output checksum is valid
+ * (usefule when the checksum is generated by the application, not the stack) */
+#ifndef TCP_CHECKSUM_ON_COPY_SANITY_CHECK
+#define TCP_CHECKSUM_ON_COPY_SANITY_CHECK 0
+#endif
+/* Allow to override the failure of sanity check from warning to e.g. hard failure */
+#if TCP_CHECKSUM_ON_COPY_SANITY_CHECK
+#ifndef TCP_CHECKSUM_ON_COPY_SANITY_CHECK_FAIL
+#define TCP_CHECKSUM_ON_COPY_SANITY_CHECK_FAIL(msg) LWIP_DEBUGF(TCP_DEBUG | LWIP_DBG_LEVEL_WARNING, msg)
+#endif
+#endif
+
+#if TCP_OVERSIZE
+/** The size of segment pbufs created when TCP_OVERSIZE is enabled */
+#ifndef TCP_OVERSIZE_CALC_LENGTH
+#define TCP_OVERSIZE_CALC_LENGTH(length) ((length) + TCP_OVERSIZE)
+#endif
+#endif
+
+/* Forward declarations.*/
+static err_t tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb, struct netif *netif);
+
+/** Allocate a pbuf and create a tcphdr at p->payload, used for output
+ * functions other than the default tcp_output -> tcp_output_segment
+ * (e.g. tcp_send_empty_ack, etc.)
+ *
+ * @param pcb tcp pcb for which to send a packet (used to initialize tcp_hdr)
+ * @param optlen length of header-options
+ * @param datalen length of tcp data to reserve in pbuf
+ * @param seqno_be seqno in network byte order (big-endian)
+ * @return pbuf with p->payload being the tcp_hdr
+ */
+static struct pbuf *
+tcp_output_alloc_header(struct tcp_pcb *pcb, u16_t optlen, u16_t datalen,
+ u32_t seqno_be /* already in network byte order */)
+{
+ struct tcp_hdr *tcphdr;
+ struct pbuf *p = pbuf_alloc(PBUF_IP, TCP_HLEN + optlen + datalen, PBUF_RAM);
+ if (p != NULL) {
+ LWIP_ASSERT("check that first pbuf can hold struct tcp_hdr",
+ (p->len >= TCP_HLEN + optlen));
+ tcphdr = (struct tcp_hdr *)p->payload;
+ tcphdr->src = lwip_htons(pcb->local_port);
+ tcphdr->dest = lwip_htons(pcb->remote_port);
+ tcphdr->seqno = seqno_be;
+ tcphdr->ackno = lwip_htonl(pcb->rcv_nxt);
+ TCPH_HDRLEN_FLAGS_SET(tcphdr, (5 + optlen / 4), TCP_ACK);
+ tcphdr->wnd = lwip_htons(TCPWND_MIN16(RCV_WND_SCALE(pcb, pcb->rcv_ann_wnd)));
+ tcphdr->chksum = 0;
+ tcphdr->urgp = 0;
+
+ /* If we're sending a packet, update the announced right window edge */
+ pcb->rcv_ann_right_edge = pcb->rcv_nxt + pcb->rcv_ann_wnd;
+ }
+ return p;
+}
+
+/**
+ * Called by tcp_close() to send a segment including FIN flag but not data.
+ *
+ * @param pcb the tcp_pcb over which to send a segment
+ * @return ERR_OK if sent, another err_t otherwise
+ */
+err_t
+tcp_send_fin(struct tcp_pcb *pcb)
+{
+ /* first, try to add the fin to the last unsent segment */
+ if (pcb->unsent != NULL) {
+ struct tcp_seg *last_unsent;
+ for (last_unsent = pcb->unsent; last_unsent->next != NULL;
+ last_unsent = last_unsent->next);
+
+ if ((TCPH_FLAGS(last_unsent->tcphdr) & (TCP_SYN | TCP_FIN | TCP_RST)) == 0) {
+ /* no SYN/FIN/RST flag in the header, we can add the FIN flag */
+ TCPH_SET_FLAG(last_unsent->tcphdr, TCP_FIN);
+ pcb->flags |= TF_FIN;
+ return ERR_OK;
+ }
+ }
+ /* no data, no length, flags, copy=1, no optdata */
+ return tcp_enqueue_flags(pcb, TCP_FIN);
+}
+
+/**
+ * Create a TCP segment with prefilled header.
+ *
+ * Called by tcp_write and tcp_enqueue_flags.
+ *
+ * @param pcb Protocol control block for the TCP connection.
+ * @param p pbuf that is used to hold the TCP header.
+ * @param flags TCP flags for header.
+ * @param seqno TCP sequence number of this packet
+ * @param optflags options to include in TCP header
+ * @return a new tcp_seg pointing to p, or NULL.
+ * The TCP header is filled in except ackno and wnd.
+ * p is freed on failure.
+ */
+static struct tcp_seg *
+tcp_create_segment(struct tcp_pcb *pcb, struct pbuf *p, u8_t flags, u32_t seqno, u8_t optflags)
+{
+ struct tcp_seg *seg;
+ u8_t optlen = LWIP_TCP_OPT_LENGTH(optflags);
+
+ if ((seg = (struct tcp_seg *)memp_malloc(MEMP_TCP_SEG)) == NULL) {
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("tcp_create_segment: no memory.\n"));
+ pbuf_free(p);
+ return NULL;
+ }
+ seg->flags = optflags;
+ seg->next = NULL;
+ seg->p = p;
+ LWIP_ASSERT("p->tot_len >= optlen", p->tot_len >= optlen);
+ seg->len = p->tot_len - optlen;
+#if TCP_OVERSIZE_DBGCHECK
+ seg->oversize_left = 0;
+#endif /* TCP_OVERSIZE_DBGCHECK */
+#if TCP_CHECKSUM_ON_COPY
+ seg->chksum = 0;
+ seg->chksum_swapped = 0;
+ /* check optflags */
+ LWIP_ASSERT("invalid optflags passed: TF_SEG_DATA_CHECKSUMMED",
+ (optflags & TF_SEG_DATA_CHECKSUMMED) == 0);
+#endif /* TCP_CHECKSUM_ON_COPY */
+
+ /* build TCP header */
+ if (pbuf_header(p, TCP_HLEN)) {
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("tcp_create_segment: no room for TCP header in pbuf.\n"));
+ TCP_STATS_INC(tcp.err);
+ tcp_seg_free(seg);
+ return NULL;
+ }
+ seg->tcphdr = (struct tcp_hdr *)seg->p->payload;
+ seg->tcphdr->src = lwip_htons(pcb->local_port);
+ seg->tcphdr->dest = lwip_htons(pcb->remote_port);
+ seg->tcphdr->seqno = lwip_htonl(seqno);
+ /* ackno is set in tcp_output */
+ TCPH_HDRLEN_FLAGS_SET(seg->tcphdr, (5 + optlen / 4), flags);
+ /* wnd and chksum are set in tcp_output */
+ seg->tcphdr->urgp = 0;
+ return seg;
+}
+
+/**
+ * Allocate a PBUF_RAM pbuf, perhaps with extra space at the end.
+ *
+ * This function is like pbuf_alloc(layer, length, PBUF_RAM) except
+ * there may be extra bytes available at the end.
+ *
+ * @param layer flag to define header size.
+ * @param length size of the pbuf's payload.
+ * @param max_length maximum usable size of payload+oversize.
+ * @param oversize pointer to a u16_t that will receive the number of usable tail bytes.
+ * @param pcb The TCP connection that will enqueue the pbuf.
+ * @param apiflags API flags given to tcp_write.
+ * @param first_seg true when this pbuf will be used in the first enqueued segment.
+ */
+#if TCP_OVERSIZE
+static struct pbuf *
+tcp_pbuf_prealloc(pbuf_layer layer, u16_t length, u16_t max_length,
+ u16_t *oversize, struct tcp_pcb *pcb, u8_t apiflags,
+ u8_t first_seg)
+{
+ struct pbuf *p;
+ u16_t alloc = length;
+
+#if LWIP_NETIF_TX_SINGLE_PBUF
+ LWIP_UNUSED_ARG(max_length);
+ LWIP_UNUSED_ARG(pcb);
+ LWIP_UNUSED_ARG(apiflags);
+ LWIP_UNUSED_ARG(first_seg);
+ alloc = max_length;
+#else /* LWIP_NETIF_TX_SINGLE_PBUF */
+ if (length < max_length) {
+ /* Should we allocate an oversized pbuf, or just the minimum
+ * length required? If tcp_write is going to be called again
+ * before this segment is transmitted, we want the oversized
+ * buffer. If the segment will be transmitted immediately, we can
+ * save memory by allocating only length. We use a simple
+ * heuristic based on the following information:
+ *
+ * Did the user set TCP_WRITE_FLAG_MORE?
+ *
+ * Will the Nagle algorithm defer transmission of this segment?
+ */
+ if ((apiflags & TCP_WRITE_FLAG_MORE) ||
+ (!(pcb->flags & TF_NODELAY) &&
+ (!first_seg ||
+ pcb->unsent != NULL ||
+ pcb->unacked != NULL))) {
+ alloc = LWIP_MIN(max_length, LWIP_MEM_ALIGN_SIZE(TCP_OVERSIZE_CALC_LENGTH(length)));
+ }
+ }
+#endif /* LWIP_NETIF_TX_SINGLE_PBUF */
+ p = pbuf_alloc(layer, alloc, PBUF_RAM);
+ if (p == NULL) {
+ return NULL;
+ }
+ LWIP_ASSERT("need unchained pbuf", p->next == NULL);
+ *oversize = p->len - length;
+ /* trim p->len to the currently used size */
+ p->len = p->tot_len = length;
+ return p;
+}
+#else /* TCP_OVERSIZE */
+#define tcp_pbuf_prealloc(layer, length, mx, os, pcb, api, fst) pbuf_alloc((layer), (length), PBUF_RAM)
+#endif /* TCP_OVERSIZE */
+
+#if TCP_CHECKSUM_ON_COPY
+/** Add a checksum of newly added data to the segment */
+static void
+tcp_seg_add_chksum(u16_t chksum, u16_t len, u16_t *seg_chksum,
+ u8_t *seg_chksum_swapped)
+{
+ u32_t helper;
+ /* add chksum to old chksum and fold to u16_t */
+ helper = chksum + *seg_chksum;
+ chksum = FOLD_U32T(helper);
+ if ((len & 1) != 0) {
+ *seg_chksum_swapped = 1 - *seg_chksum_swapped;
+ chksum = SWAP_BYTES_IN_WORD(chksum);
+ }
+ *seg_chksum = chksum;
+}
+#endif /* TCP_CHECKSUM_ON_COPY */
+
+/** Checks if tcp_write is allowed or not (checks state, snd_buf and snd_queuelen).
+ *
+ * @param pcb the tcp pcb to check for
+ * @param len length of data to send (checked agains snd_buf)
+ * @return ERR_OK if tcp_write is allowed to proceed, another err_t otherwise
+ */
+static err_t
+tcp_write_checks(struct tcp_pcb *pcb, u16_t len)
+{
+ /* connection is in invalid state for data transmission? */
+ if ((pcb->state != ESTABLISHED) &&
+ (pcb->state != CLOSE_WAIT) &&
+ (pcb->state != SYN_SENT) &&
+ (pcb->state != SYN_RCVD)) {
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_STATE | LWIP_DBG_LEVEL_SEVERE, ("tcp_write() called in invalid state\n"));
+ return ERR_CONN;
+ } else if (len == 0) {
+ return ERR_OK;
+ }
+
+ /* fail on too much data */
+ if (len > pcb->snd_buf) {
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SEVERE, ("tcp_write: too much data (len=%"U16_F" > snd_buf=%"TCPWNDSIZE_F")\n",
+ len, pcb->snd_buf));
+ pcb->flags |= TF_NAGLEMEMERR;
+ return ERR_MEM;
+ }
+
+ LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_write: queuelen: %"TCPWNDSIZE_F"\n", (tcpwnd_size_t)pcb->snd_queuelen));
+
+ /* If total number of pbufs on the unsent/unacked queues exceeds the
+ * configured maximum, return an error */
+ /* check for configured max queuelen and possible overflow */
+ if ((pcb->snd_queuelen >= TCP_SND_QUEUELEN) || (pcb->snd_queuelen > TCP_SNDQUEUELEN_OVERFLOW)) {
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SEVERE, ("tcp_write: too long queue %"U16_F" (max %"U16_F")\n",
+ pcb->snd_queuelen, (u16_t)TCP_SND_QUEUELEN));
+ TCP_STATS_INC(tcp.memerr);
+ pcb->flags |= TF_NAGLEMEMERR;
+ return ERR_MEM;
+ }
+ if (pcb->snd_queuelen != 0) {
+ LWIP_ASSERT("tcp_write: pbufs on queue => at least one queue non-empty",
+ pcb->unacked != NULL || pcb->unsent != NULL);
+ } else {
+ LWIP_ASSERT("tcp_write: no pbufs on queue => both queues empty",
+ pcb->unacked == NULL && pcb->unsent == NULL);
+ }
+ return ERR_OK;
+}
+
+/**
+ * @ingroup tcp_raw
+ * Write data for sending (but does not send it immediately).
+ *
+ * It waits in the expectation of more data being sent soon (as
+ * it can send them more efficiently by combining them together).
+ * To prompt the system to send data now, call tcp_output() after
+ * calling tcp_write().
+ *
+ * @param pcb Protocol control block for the TCP connection to enqueue data for.
+ * @param arg Pointer to the data to be enqueued for sending.
+ * @param len Data length in bytes
+ * @param apiflags combination of following flags :
+ * - TCP_WRITE_FLAG_COPY (0x01) data will be copied into memory belonging to the stack
+ * - TCP_WRITE_FLAG_MORE (0x02) for TCP connection, PSH flag will not be set on last segment sent,
+ * @return ERR_OK if enqueued, another err_t on error
+ */
+err_t
+tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags)
+{
+ struct pbuf *concat_p = NULL;
+ struct tcp_seg *last_unsent = NULL, *seg = NULL, *prev_seg = NULL, *queue = NULL;
+ u16_t pos = 0; /* position in 'arg' data */
+ u16_t queuelen;
+ u8_t optlen = 0;
+ u8_t optflags = 0;
+#if TCP_OVERSIZE
+ u16_t oversize = 0;
+ u16_t oversize_used = 0;
+#if TCP_OVERSIZE_DBGCHECK
+ u16_t oversize_add = 0;
+#endif /* TCP_OVERSIZE_DBGCHECK*/
+#endif /* TCP_OVERSIZE */
+ u16_t extendlen = 0;
+#if TCP_CHECKSUM_ON_COPY
+ u16_t concat_chksum = 0;
+ u8_t concat_chksum_swapped = 0;
+ u16_t concat_chksummed = 0;
+#endif /* TCP_CHECKSUM_ON_COPY */
+ err_t err;
+ /* don't allocate segments bigger than half the maximum window we ever received */
+ u16_t mss_local = LWIP_MIN(pcb->mss, TCPWND_MIN16(pcb->snd_wnd_max/2));
+ mss_local = mss_local ? mss_local : pcb->mss;
+
+#if LWIP_NETIF_TX_SINGLE_PBUF
+ /* Always copy to try to create single pbufs for TX */
+ apiflags |= TCP_WRITE_FLAG_COPY;
+#endif /* LWIP_NETIF_TX_SINGLE_PBUF */
+
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_write(pcb=%p, data=%p, len=%"U16_F", apiflags=%"U16_F")\n",
+ (void *)pcb, arg, len, (u16_t)apiflags));
+ LWIP_ERROR("tcp_write: arg == NULL (programmer violates API)",
+ arg != NULL, return ERR_ARG;);
+
+ err = tcp_write_checks(pcb, len);
+ if (err != ERR_OK) {
+ return err;
+ }
+ queuelen = pcb->snd_queuelen;
+
+#if LWIP_TCP_TIMESTAMPS
+ if ((pcb->flags & TF_TIMESTAMP)) {
+ /* Make sure the timestamp option is only included in data segments if we
+ agreed about it with the remote host. */
+ optflags = TF_SEG_OPTS_TS;
+ optlen = LWIP_TCP_OPT_LENGTH(TF_SEG_OPTS_TS);
+ /* ensure that segments can hold at least one data byte... */
+ mss_local = LWIP_MAX(mss_local, LWIP_TCP_OPT_LEN_TS + 1);
+ }
+#endif /* LWIP_TCP_TIMESTAMPS */
+
+
+ /*
+ * TCP segmentation is done in three phases with increasing complexity:
+ *
+ * 1. Copy data directly into an oversized pbuf.
+ * 2. Chain a new pbuf to the end of pcb->unsent.
+ * 3. Create new segments.
+ *
+ * We may run out of memory at any point. In that case we must
+ * return ERR_MEM and not change anything in pcb. Therefore, all
+ * changes are recorded in local variables and committed at the end
+ * of the function. Some pcb fields are maintained in local copies:
+ *
+ * queuelen = pcb->snd_queuelen
+ * oversize = pcb->unsent_oversize
+ *
+ * These variables are set consistently by the phases:
+ *
+ * seg points to the last segment tampered with.
+ *
+ * pos records progress as data is segmented.
+ */
+
+ /* Find the tail of the unsent queue. */
+ if (pcb->unsent != NULL) {
+ u16_t space;
+ u16_t unsent_optlen;
+
+ /* @todo: this could be sped up by keeping last_unsent in the pcb */
+ for (last_unsent = pcb->unsent; last_unsent->next != NULL;
+ last_unsent = last_unsent->next);
+
+ /* Usable space at the end of the last unsent segment */
+ unsent_optlen = LWIP_TCP_OPT_LENGTH(last_unsent->flags);
+ LWIP_ASSERT("mss_local is too small", mss_local >= last_unsent->len + unsent_optlen);
+ space = mss_local - (last_unsent->len + unsent_optlen);
+
+ /*
+ * Phase 1: Copy data directly into an oversized pbuf.
+ *
+ * The number of bytes copied is recorded in the oversize_used
+ * variable. The actual copying is done at the bottom of the
+ * function.
+ */
+#if TCP_OVERSIZE
+#if TCP_OVERSIZE_DBGCHECK
+ /* check that pcb->unsent_oversize matches last_unsent->oversize_left */
+ LWIP_ASSERT("unsent_oversize mismatch (pcb vs. last_unsent)",
+ pcb->unsent_oversize == last_unsent->oversize_left);
+#endif /* TCP_OVERSIZE_DBGCHECK */
+ oversize = pcb->unsent_oversize;
+ if (oversize > 0) {
+ LWIP_ASSERT("inconsistent oversize vs. space", oversize <= space);
+ seg = last_unsent;
+ oversize_used = LWIP_MIN(space, LWIP_MIN(oversize, len));
+ pos += oversize_used;
+ oversize -= oversize_used;
+ space -= oversize_used;
+ }
+ /* now we are either finished or oversize is zero */
+ LWIP_ASSERT("inconsistent oversize vs. len", (oversize == 0) || (pos == len));
+#endif /* TCP_OVERSIZE */
+
+ /*
+ * Phase 2: Chain a new pbuf to the end of pcb->unsent.
+ *
+ * As an exception when NOT copying the data, if the given data buffer
+ * directly follows the last unsent data buffer in memory, extend the last
+ * ROM pbuf reference to the buffer, thus saving a ROM pbuf allocation.
+ *
+ * We don't extend segments containing SYN/FIN flags or options
+ * (len==0). The new pbuf is kept in concat_p and pbuf_cat'ed at
+ * the end.
+ */
+ if ((pos < len) && (space > 0) && (last_unsent->len > 0)) {
+ u16_t seglen = LWIP_MIN(space, len - pos);
+ seg = last_unsent;
+
+ /* Create a pbuf with a copy or reference to seglen bytes. We
+ * can use PBUF_RAW here since the data appears in the middle of
+ * a segment. A header will never be prepended. */
+ if (apiflags & TCP_WRITE_FLAG_COPY) {
+ /* Data is copied */
+ if ((concat_p = tcp_pbuf_prealloc(PBUF_RAW, seglen, space, &oversize, pcb, apiflags, 1)) == NULL) {
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+ ("tcp_write : could not allocate memory for pbuf copy size %"U16_F"\n",
+ seglen));
+ goto memerr;
+ }
+#if TCP_OVERSIZE_DBGCHECK
+ oversize_add = oversize;
+#endif /* TCP_OVERSIZE_DBGCHECK */
+ TCP_DATA_COPY2(concat_p->payload, (const u8_t*)arg + pos, seglen, &concat_chksum, &concat_chksum_swapped);
+#if TCP_CHECKSUM_ON_COPY
+ concat_chksummed += seglen;
+#endif /* TCP_CHECKSUM_ON_COPY */
+ queuelen += pbuf_clen(concat_p);
+ } else {
+ /* Data is not copied */
+ /* If the last unsent pbuf is of type PBUF_ROM, try to extend it. */
+ struct pbuf *p;
+ for (p = last_unsent->p; p->next != NULL; p = p->next);
+ if (p->type == PBUF_ROM && (const u8_t *)p->payload + p->len == (const u8_t *)arg) {
+ LWIP_ASSERT("tcp_write: ROM pbufs cannot be oversized", pos == 0);
+ extendlen = seglen;
+ } else {
+ if ((concat_p = pbuf_alloc(PBUF_RAW, seglen, PBUF_ROM)) == NULL) {
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+ ("tcp_write: could not allocate memory for zero-copy pbuf\n"));
+ goto memerr;
+ }
+ /* reference the non-volatile payload data */
+ ((struct pbuf_rom*)concat_p)->payload = (const u8_t*)arg + pos;
+ queuelen += pbuf_clen(concat_p);
+ }
+#if TCP_CHECKSUM_ON_COPY
+ /* calculate the checksum of nocopy-data */
+ tcp_seg_add_chksum(~inet_chksum((const u8_t*)arg + pos, seglen), seglen,
+ &concat_chksum, &concat_chksum_swapped);
+ concat_chksummed += seglen;
+#endif /* TCP_CHECKSUM_ON_COPY */
+ }
+
+ pos += seglen;
+ }
+ } else {
+#if TCP_OVERSIZE
+ LWIP_ASSERT("unsent_oversize mismatch (pcb->unsent is NULL)",
+ pcb->unsent_oversize == 0);
+#endif /* TCP_OVERSIZE */
+ }
+
+ /*
+ * Phase 3: Create new segments.
+ *
+ * The new segments are chained together in the local 'queue'
+ * variable, ready to be appended to pcb->unsent.
+ */
+ while (pos < len) {
+ struct pbuf *p;
+ u16_t left = len - pos;
+ u16_t max_len = mss_local - optlen;
+ u16_t seglen = LWIP_MIN(left, max_len);
+#if TCP_CHECKSUM_ON_COPY
+ u16_t chksum = 0;
+ u8_t chksum_swapped = 0;
+#endif /* TCP_CHECKSUM_ON_COPY */
+
+ if (apiflags & TCP_WRITE_FLAG_COPY) {
+ /* If copy is set, memory should be allocated and data copied
+ * into pbuf */
+ if ((p = tcp_pbuf_prealloc(PBUF_TRANSPORT, seglen + optlen, mss_local, &oversize, pcb, apiflags, queue == NULL)) == NULL) {
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("tcp_write : could not allocate memory for pbuf copy size %"U16_F"\n", seglen));
+ goto memerr;
+ }
+ LWIP_ASSERT("tcp_write: check that first pbuf can hold the complete seglen",
+ (p->len >= seglen));
+ TCP_DATA_COPY2((char *)p->payload + optlen, (const u8_t*)arg + pos, seglen, &chksum, &chksum_swapped);
+ } else {
+ /* Copy is not set: First allocate a pbuf for holding the data.
+ * Since the referenced data is available at least until it is
+ * sent out on the link (as it has to be ACKed by the remote
+ * party) we can safely use PBUF_ROM instead of PBUF_REF here.
+ */
+ struct pbuf *p2;
+#if TCP_OVERSIZE
+ LWIP_ASSERT("oversize == 0", oversize == 0);
+#endif /* TCP_OVERSIZE */
+ if ((p2 = pbuf_alloc(PBUF_TRANSPORT, seglen, PBUF_ROM)) == NULL) {
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("tcp_write: could not allocate memory for zero-copy pbuf\n"));
+ goto memerr;
+ }
+#if TCP_CHECKSUM_ON_COPY
+ /* calculate the checksum of nocopy-data */
+ chksum = ~inet_chksum((const u8_t*)arg + pos, seglen);
+ if (seglen & 1) {
+ chksum_swapped = 1;
+ chksum = SWAP_BYTES_IN_WORD(chksum);
+ }
+#endif /* TCP_CHECKSUM_ON_COPY */
+ /* reference the non-volatile payload data */
+ ((struct pbuf_rom*)p2)->payload = (const u8_t*)arg + pos;
+
+ /* Second, allocate a pbuf for the headers. */
+ if ((p = pbuf_alloc(PBUF_TRANSPORT, optlen, PBUF_RAM)) == NULL) {
+ /* If allocation fails, we have to deallocate the data pbuf as
+ * well. */
+ pbuf_free(p2);
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("tcp_write: could not allocate memory for header pbuf\n"));
+ goto memerr;
+ }
+ /* Concatenate the headers and data pbufs together. */
+ pbuf_cat(p/*header*/, p2/*data*/);
+ }
+
+ queuelen += pbuf_clen(p);
+
+ /* Now that there are more segments queued, we check again if the
+ * length of the queue exceeds the configured maximum or
+ * overflows. */
+ if ((queuelen > TCP_SND_QUEUELEN) || (queuelen > TCP_SNDQUEUELEN_OVERFLOW)) {
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("tcp_write: queue too long %"U16_F" (%d)\n",
+ queuelen, (int)TCP_SND_QUEUELEN));
+ pbuf_free(p);
+ goto memerr;
+ }
+
+ if ((seg = tcp_create_segment(pcb, p, 0, pcb->snd_lbb + pos, optflags)) == NULL) {
+ goto memerr;
+ }
+#if TCP_OVERSIZE_DBGCHECK
+ seg->oversize_left = oversize;
+#endif /* TCP_OVERSIZE_DBGCHECK */
+#if TCP_CHECKSUM_ON_COPY
+ seg->chksum = chksum;
+ seg->chksum_swapped = chksum_swapped;
+ seg->flags |= TF_SEG_DATA_CHECKSUMMED;
+#endif /* TCP_CHECKSUM_ON_COPY */
+
+ /* first segment of to-be-queued data? */
+ if (queue == NULL) {
+ queue = seg;
+ } else {
+ /* Attach the segment to the end of the queued segments */
+ LWIP_ASSERT("prev_seg != NULL", prev_seg != NULL);
+ prev_seg->next = seg;
+ }
+ /* remember last segment of to-be-queued data for next iteration */
+ prev_seg = seg;
+
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_TRACE, ("tcp_write: queueing %"U32_F":%"U32_F"\n",
+ lwip_ntohl(seg->tcphdr->seqno),
+ lwip_ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg)));
+
+ pos += seglen;
+ }
+
+ /*
+ * All three segmentation phases were successful. We can commit the
+ * transaction.
+ */
+#if TCP_OVERSIZE_DBGCHECK
+ if ((last_unsent != NULL) && (oversize_add != 0)) {
+ last_unsent->oversize_left += oversize_add;
+ }
+#endif /* TCP_OVERSIZE_DBGCHECK */
+
+ /*
+ * Phase 1: If data has been added to the preallocated tail of
+ * last_unsent, we update the length fields of the pbuf chain.
+ */
+#if TCP_OVERSIZE
+ if (oversize_used > 0) {
+ struct pbuf *p;
+ /* Bump tot_len of whole chain, len of tail */
+ for (p = last_unsent->p; p; p = p->next) {
+ p->tot_len += oversize_used;
+ if (p->next == NULL) {
+ TCP_DATA_COPY((char *)p->payload + p->len, arg, oversize_used, last_unsent);
+ p->len += oversize_used;
+ }
+ }
+ last_unsent->len += oversize_used;
+#if TCP_OVERSIZE_DBGCHECK
+ LWIP_ASSERT("last_unsent->oversize_left >= oversize_used",
+ last_unsent->oversize_left >= oversize_used);
+ last_unsent->oversize_left -= oversize_used;
+#endif /* TCP_OVERSIZE_DBGCHECK */
+ }
+ pcb->unsent_oversize = oversize;
+#endif /* TCP_OVERSIZE */
+
+ /*
+ * Phase 2: concat_p can be concatenated onto last_unsent->p, unless we
+ * determined that the last ROM pbuf can be extended to include the new data.
+ */
+ if (concat_p != NULL) {
+ LWIP_ASSERT("tcp_write: cannot concatenate when pcb->unsent is empty",
+ (last_unsent != NULL));
+ pbuf_cat(last_unsent->p, concat_p);
+ last_unsent->len += concat_p->tot_len;
+ } else if (extendlen > 0) {
+ struct pbuf *p;
+ LWIP_ASSERT("tcp_write: extension of reference requires reference",
+ last_unsent != NULL && last_unsent->p != NULL);
+ for (p = last_unsent->p; p->next != NULL; p = p->next) {
+ p->tot_len += extendlen;
+ }
+ p->tot_len += extendlen;
+ p->len += extendlen;
+ last_unsent->len += extendlen;
+ }
+
+#if TCP_CHECKSUM_ON_COPY
+ if (concat_chksummed) {
+ LWIP_ASSERT("tcp_write: concat checksum needs concatenated data",
+ concat_p != NULL || extendlen > 0);
+ /*if concat checksumm swapped - swap it back */
+ if (concat_chksum_swapped) {
+ concat_chksum = SWAP_BYTES_IN_WORD(concat_chksum);
+ }
+ tcp_seg_add_chksum(concat_chksum, concat_chksummed, &last_unsent->chksum,
+ &last_unsent->chksum_swapped);
+ last_unsent->flags |= TF_SEG_DATA_CHECKSUMMED;
+ }
+#endif /* TCP_CHECKSUM_ON_COPY */
+
+ /*
+ * Phase 3: Append queue to pcb->unsent. Queue may be NULL, but that
+ * is harmless
+ */
+ if (last_unsent == NULL) {
+ pcb->unsent = queue;
+ } else {
+ last_unsent->next = queue;
+ }
+
+ /*
+ * Finally update the pcb state.
+ */
+ pcb->snd_lbb += len;
+ pcb->snd_buf -= len;
+ pcb->snd_queuelen = queuelen;
+
+ LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_write: %"S16_F" (after enqueued)\n",
+ pcb->snd_queuelen));
+ if (pcb->snd_queuelen != 0) {
+ LWIP_ASSERT("tcp_write: valid queue length",
+ pcb->unacked != NULL || pcb->unsent != NULL);
+ }
+
+ /* Set the PSH flag in the last segment that we enqueued. */
+ if (seg != NULL && seg->tcphdr != NULL && ((apiflags & TCP_WRITE_FLAG_MORE)==0)) {
+ TCPH_SET_FLAG(seg->tcphdr, TCP_PSH);
+ }
+
+ return ERR_OK;
+memerr:
+ pcb->flags |= TF_NAGLEMEMERR;
+ TCP_STATS_INC(tcp.memerr);
+
+ if (concat_p != NULL) {
+ pbuf_free(concat_p);
+ }
+ if (queue != NULL) {
+ tcp_segs_free(queue);
+ }
+ if (pcb->snd_queuelen != 0) {
+ LWIP_ASSERT("tcp_write: valid queue length", pcb->unacked != NULL ||
+ pcb->unsent != NULL);
+ }
+ LWIP_DEBUGF(TCP_QLEN_DEBUG | LWIP_DBG_STATE, ("tcp_write: %"S16_F" (with mem err)\n", pcb->snd_queuelen));
+ return ERR_MEM;
+}
+
+/**
+ * Enqueue TCP options for transmission.
+ *
+ * Called by tcp_connect(), tcp_listen_input(), and tcp_send_ctrl().
+ *
+ * @param pcb Protocol control block for the TCP connection.
+ * @param flags TCP header flags to set in the outgoing segment.
+ */
+err_t
+tcp_enqueue_flags(struct tcp_pcb *pcb, u8_t flags)
+{
+ struct pbuf *p;
+ struct tcp_seg *seg;
+ u8_t optflags = 0;
+ u8_t optlen = 0;
+
+ LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_enqueue_flags: queuelen: %"U16_F"\n", (u16_t)pcb->snd_queuelen));
+
+ LWIP_ASSERT("tcp_enqueue_flags: need either TCP_SYN or TCP_FIN in flags (programmer violates API)",
+ (flags & (TCP_SYN | TCP_FIN)) != 0);
+
+ /* check for configured max queuelen and possible overflow (FIN flag should always come through!) */
+ if (((pcb->snd_queuelen >= TCP_SND_QUEUELEN) || (pcb->snd_queuelen > TCP_SNDQUEUELEN_OVERFLOW)) &&
+ ((flags & TCP_FIN) == 0)) {
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SEVERE, ("tcp_enqueue_flags: too long queue %"U16_F" (max %"U16_F")\n",
+ pcb->snd_queuelen, (u16_t)TCP_SND_QUEUELEN));
+ TCP_STATS_INC(tcp.memerr);
+ pcb->flags |= TF_NAGLEMEMERR;
+ return ERR_MEM;
+ }
+
+ if (flags & TCP_SYN) {
+ optflags = TF_SEG_OPTS_MSS;
+#if LWIP_WND_SCALE
+ if ((pcb->state != SYN_RCVD) || (pcb->flags & TF_WND_SCALE)) {
+ /* In a <SYN,ACK> (sent in state SYN_RCVD), the window scale option may only
+ be sent if we received a window scale option from the remote host. */
+ optflags |= TF_SEG_OPTS_WND_SCALE;
+ }
+#endif /* LWIP_WND_SCALE */
+ }
+#if LWIP_TCP_TIMESTAMPS
+ if ((pcb->flags & TF_TIMESTAMP)) {
+ /* Make sure the timestamp option is only included in data segments if we
+ agreed about it with the remote host. */
+ optflags |= TF_SEG_OPTS_TS;
+ }
+#endif /* LWIP_TCP_TIMESTAMPS */
+ optlen = LWIP_TCP_OPT_LENGTH(optflags);
+
+ /* Allocate pbuf with room for TCP header + options */
+ if ((p = pbuf_alloc(PBUF_TRANSPORT, optlen, PBUF_RAM)) == NULL) {
+ pcb->flags |= TF_NAGLEMEMERR;
+ TCP_STATS_INC(tcp.memerr);
+ return ERR_MEM;
+ }
+ LWIP_ASSERT("tcp_enqueue_flags: check that first pbuf can hold optlen",
+ (p->len >= optlen));
+
+ /* Allocate memory for tcp_seg, and fill in fields. */
+ if ((seg = tcp_create_segment(pcb, p, flags, pcb->snd_lbb, optflags)) == NULL) {
+ pcb->flags |= TF_NAGLEMEMERR;
+ TCP_STATS_INC(tcp.memerr);
+ return ERR_MEM;
+ }
+ LWIP_ASSERT("seg->tcphdr not aligned", ((mem_ptr_t)seg->tcphdr % LWIP_MIN(MEM_ALIGNMENT, 4)) == 0);
+ LWIP_ASSERT("tcp_enqueue_flags: invalid segment length", seg->len == 0);
+
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_TRACE,
+ ("tcp_enqueue_flags: queueing %"U32_F":%"U32_F" (0x%"X16_F")\n",
+ lwip_ntohl(seg->tcphdr->seqno),
+ lwip_ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg),
+ (u16_t)flags));
+
+ /* Now append seg to pcb->unsent queue */
+ if (pcb->unsent == NULL) {
+ pcb->unsent = seg;
+ } else {
+ struct tcp_seg *useg;
+ for (useg = pcb->unsent; useg->next != NULL; useg = useg->next);
+ useg->next = seg;
+ }
+#if TCP_OVERSIZE
+ /* The new unsent tail has no space */
+ pcb->unsent_oversize = 0;
+#endif /* TCP_OVERSIZE */
+
+ /* SYN and FIN bump the sequence number */
+ if ((flags & TCP_SYN) || (flags & TCP_FIN)) {
+ pcb->snd_lbb++;
+ /* optlen does not influence snd_buf */
+ }
+ if (flags & TCP_FIN) {
+ pcb->flags |= TF_FIN;
+ }
+
+ /* update number of segments on the queues */
+ pcb->snd_queuelen += pbuf_clen(seg->p);
+ LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_enqueue_flags: %"S16_F" (after enqueued)\n", pcb->snd_queuelen));
+ if (pcb->snd_queuelen != 0) {
+ LWIP_ASSERT("tcp_enqueue_flags: invalid queue length",
+ pcb->unacked != NULL || pcb->unsent != NULL);
+ }
+
+ return ERR_OK;
+}
+
+#if LWIP_TCP_TIMESTAMPS
+/* Build a timestamp option (12 bytes long) at the specified options pointer)
+ *
+ * @param pcb tcp_pcb
+ * @param opts option pointer where to store the timestamp option
+ */
+static void
+tcp_build_timestamp_option(struct tcp_pcb *pcb, u32_t *opts)
+{
+ /* Pad with two NOP options to make everything nicely aligned */
+ opts[0] = PP_HTONL(0x0101080A);
+ opts[1] = lwip_htonl(sys_now());
+ opts[2] = lwip_htonl(pcb->ts_recent);
+}
+#endif
+
+#if LWIP_WND_SCALE
+/** Build a window scale option (3 bytes long) at the specified options pointer)
+ *
+ * @param opts option pointer where to store the window scale option
+ */
+static void
+tcp_build_wnd_scale_option(u32_t *opts)
+{
+ /* Pad with one NOP option to make everything nicely aligned */
+ opts[0] = PP_HTONL(0x01030300 | TCP_RCV_SCALE);
+}
+#endif
+
+/**
+ * Send an ACK without data.
+ *
+ * @param pcb Protocol control block for the TCP connection to send the ACK
+ */
+err_t
+tcp_send_empty_ack(struct tcp_pcb *pcb)
+{
+ err_t err;
+ struct pbuf *p;
+ u8_t optlen = 0;
+ struct netif *netif;
+#if LWIP_TCP_TIMESTAMPS || CHECKSUM_GEN_TCP
+ struct tcp_hdr *tcphdr;
+#endif /* LWIP_TCP_TIMESTAMPS || CHECKSUM_GEN_TCP */
+
+#if LWIP_TCP_TIMESTAMPS
+ if (pcb->flags & TF_TIMESTAMP) {
+ optlen = LWIP_TCP_OPT_LENGTH(TF_SEG_OPTS_TS);
+ }
+#endif
+
+ p = tcp_output_alloc_header(pcb, optlen, 0, lwip_htonl(pcb->snd_nxt));
+ if (p == NULL) {
+ /* let tcp_fasttmr retry sending this ACK */
+ pcb->flags |= (TF_ACK_DELAY | TF_ACK_NOW);
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: (ACK) could not allocate pbuf\n"));
+ return ERR_BUF;
+ }
+#if LWIP_TCP_TIMESTAMPS || CHECKSUM_GEN_TCP
+ tcphdr = (struct tcp_hdr *)p->payload;
+#endif /* LWIP_TCP_TIMESTAMPS || CHECKSUM_GEN_TCP */
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG,
+ ("tcp_output: sending ACK for %"U32_F"\n", pcb->rcv_nxt));
+
+ /* NB. MSS and window scale options are only sent on SYNs, so ignore them here */
+#if LWIP_TCP_TIMESTAMPS
+ pcb->ts_lastacksent = pcb->rcv_nxt;
+
+ if (pcb->flags & TF_TIMESTAMP) {
+ tcp_build_timestamp_option(pcb, (u32_t *)(tcphdr + 1));
+ }
+#endif
+
+ netif = ip_route(&pcb->local_ip, &pcb->remote_ip);
+ if (netif == NULL) {
+ err = ERR_RTE;
+ } else {
+#if CHECKSUM_GEN_TCP
+ IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_TCP) {
+ tcphdr->chksum = ip_chksum_pseudo(p, IP_PROTO_TCP, p->tot_len,
+ &pcb->local_ip, &pcb->remote_ip);
+ }
+#endif
+ NETIF_SET_HWADDRHINT(netif, &(pcb->addr_hint));
+ err = ip_output_if(p, &pcb->local_ip, &pcb->remote_ip,
+ pcb->ttl, pcb->tos, IP_PROTO_TCP, netif);
+ NETIF_SET_HWADDRHINT(netif, NULL);
+ }
+ pbuf_free(p);
+
+ if (err != ERR_OK) {
+ /* let tcp_fasttmr retry sending this ACK */
+ pcb->flags |= (TF_ACK_DELAY | TF_ACK_NOW);
+ } else {
+ /* remove ACK flags from the PCB, as we sent an empty ACK now */
+ pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW);
+ }
+
+ return err;
+}
+
+/**
+ * @ingroup tcp_raw
+ * Find out what we can send and send it
+ *
+ * @param pcb Protocol control block for the TCP connection to send data
+ * @return ERR_OK if data has been sent or nothing to send
+ * another err_t on error
+ */
+err_t
+tcp_output(struct tcp_pcb *pcb)
+{
+ struct tcp_seg *seg, *useg;
+ u32_t wnd, snd_nxt;
+ err_t err;
+ struct netif *netif;
+#if TCP_CWND_DEBUG
+ s16_t i = 0;
+#endif /* TCP_CWND_DEBUG */
+
+ /* pcb->state LISTEN not allowed here */
+ LWIP_ASSERT("don't call tcp_output for listen-pcbs",
+ pcb->state != LISTEN);
+
+ /* First, check if we are invoked by the TCP input processing
+ code. If so, we do not output anything. Instead, we rely on the
+ input processing code to call us when input processing is done
+ with. */
+ if (tcp_input_pcb == pcb) {
+ return ERR_OK;
+ }
+
+ wnd = LWIP_MIN(pcb->snd_wnd, pcb->cwnd);
+
+ seg = pcb->unsent;
+
+ /* If the TF_ACK_NOW flag is set and no data will be sent (either
+ * because the ->unsent queue is empty or because the window does
+ * not allow it), construct an empty ACK segment and send it.
+ *
+ * If data is to be sent, we will just piggyback the ACK (see below).
+ */
+ if (pcb->flags & TF_ACK_NOW &&
+ (seg == NULL ||
+ lwip_ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len > wnd)) {
+ return tcp_send_empty_ack(pcb);
+ }
+
+ /* useg should point to last segment on unacked queue */
+ useg = pcb->unacked;
+ if (useg != NULL) {
+ for (; useg->next != NULL; useg = useg->next);
+ }
+
+ netif = ip_route(&pcb->local_ip, &pcb->remote_ip);
+ if (netif == NULL) {
+ return ERR_RTE;
+ }
+
+ /* If we don't have a local IP address, we get one from netif */
+ if (ip_addr_isany(&pcb->local_ip)) {
+ const ip_addr_t *local_ip = ip_netif_get_local_ip(netif, &pcb->remote_ip);
+ if (local_ip == NULL) {
+ return ERR_RTE;
+ }
+ ip_addr_copy(pcb->local_ip, *local_ip);
+ }
+
+#if TCP_OUTPUT_DEBUG
+ if (seg == NULL) {
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: nothing to send (%p)\n",
+ (void*)pcb->unsent));
+ }
+#endif /* TCP_OUTPUT_DEBUG */
+#if TCP_CWND_DEBUG
+ if (seg == NULL) {
+ LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_output: snd_wnd %"TCPWNDSIZE_F
+ ", cwnd %"TCPWNDSIZE_F", wnd %"U32_F
+ ", seg == NULL, ack %"U32_F"\n",
+ pcb->snd_wnd, pcb->cwnd, wnd, pcb->lastack));
+ } else {
+ LWIP_DEBUGF(TCP_CWND_DEBUG,
+ ("tcp_output: snd_wnd %"TCPWNDSIZE_F", cwnd %"TCPWNDSIZE_F", wnd %"U32_F
+ ", effwnd %"U32_F", seq %"U32_F", ack %"U32_F"\n",
+ pcb->snd_wnd, pcb->cwnd, wnd,
+ lwip_ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len,
+ lwip_ntohl(seg->tcphdr->seqno), pcb->lastack));
+ }
+#endif /* TCP_CWND_DEBUG */
+ /* Check if we need to start the persistent timer when the next unsent segment
+ * does not fit within the remaining send window and RTO timer is not running (we
+ * have no in-flight data). A traditional approach would fill the remaining window
+ * with part of the unsent segment (which will engage zero-window probing upon
+ * reception of the zero window update from the receiver). This ensures the
+ * subsequent window update is reliably received. With the goal of being lightweight,
+ * we avoid splitting the unsent segment and treat the window as already zero.
+ */
+ if (seg != NULL &&
+ lwip_ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len > wnd &&
+ wnd > 0 && wnd == pcb->snd_wnd && pcb->unacked == NULL) {
+ /* Start the persist timer */
+ if (pcb->persist_backoff == 0) {
+ pcb->persist_cnt = 0;
+ pcb->persist_backoff = 1;
+ }
+ goto output_done;
+ }
+ /* data available and window allows it to be sent? */
+ while (seg != NULL &&
+ lwip_ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len <= wnd) {
+ LWIP_ASSERT("RST not expected here!",
+ (TCPH_FLAGS(seg->tcphdr) & TCP_RST) == 0);
+ /* Stop sending if the nagle algorithm would prevent it
+ * Don't stop:
+ * - if tcp_write had a memory error before (prevent delayed ACK timeout) or
+ * - if FIN was already enqueued for this PCB (SYN is always alone in a segment -
+ * either seg->next != NULL or pcb->unacked == NULL;
+ * RST is no sent using tcp_write/tcp_output.
+ */
+ if ((tcp_do_output_nagle(pcb) == 0) &&
+ ((pcb->flags & (TF_NAGLEMEMERR | TF_FIN)) == 0)) {
+ break;
+ }
+#if TCP_CWND_DEBUG
+ LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_output: snd_wnd %"TCPWNDSIZE_F", cwnd %"TCPWNDSIZE_F", wnd %"U32_F", effwnd %"U32_F", seq %"U32_F", ack %"U32_F", i %"S16_F"\n",
+ pcb->snd_wnd, pcb->cwnd, wnd,
+ lwip_ntohl(seg->tcphdr->seqno) + seg->len -
+ pcb->lastack,
+ lwip_ntohl(seg->tcphdr->seqno), pcb->lastack, i));
+ ++i;
+#endif /* TCP_CWND_DEBUG */
+
+ if (pcb->state != SYN_SENT) {
+ TCPH_SET_FLAG(seg->tcphdr, TCP_ACK);
+ }
+
+#if TCP_OVERSIZE_DBGCHECK
+ seg->oversize_left = 0;
+#endif /* TCP_OVERSIZE_DBGCHECK */
+ err = tcp_output_segment(seg, pcb, netif);
+ if (err != ERR_OK) {
+ /* segment could not be sent, for whatever reason */
+ pcb->flags |= TF_NAGLEMEMERR;
+ return err;
+ }
+ pcb->unsent = seg->next;
+ if (pcb->state != SYN_SENT) {
+ pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW);
+ }
+ snd_nxt = lwip_ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg);
+ if (TCP_SEQ_LT(pcb->snd_nxt, snd_nxt)) {
+ pcb->snd_nxt = snd_nxt;
+ }
+ /* put segment on unacknowledged list if length > 0 */
+ if (TCP_TCPLEN(seg) > 0) {
+ seg->next = NULL;
+ /* unacked list is empty? */
+ if (pcb->unacked == NULL) {
+ pcb->unacked = seg;
+ useg = seg;
+ /* unacked list is not empty? */
+ } else {
+ /* In the case of fast retransmit, the packet should not go to the tail
+ * of the unacked queue, but rather somewhere before it. We need to check for
+ * this case. -STJ Jul 27, 2004 */
+ if (TCP_SEQ_LT(lwip_ntohl(seg->tcphdr->seqno), lwip_ntohl(useg->tcphdr->seqno))) {
+ /* add segment to before tail of unacked list, keeping the list sorted */
+ struct tcp_seg **cur_seg = &(pcb->unacked);
+ while (*cur_seg &&
+ TCP_SEQ_LT(lwip_ntohl((*cur_seg)->tcphdr->seqno), lwip_ntohl(seg->tcphdr->seqno))) {
+ cur_seg = &((*cur_seg)->next );
+ }
+ seg->next = (*cur_seg);
+ (*cur_seg) = seg;
+ } else {
+ /* add segment to tail of unacked list */
+ useg->next = seg;
+ useg = useg->next;
+ }
+ }
+ /* do not queue empty segments on the unacked list */
+ } else {
+ tcp_seg_free(seg);
+ }
+ seg = pcb->unsent;
+ }
+output_done:
+#if TCP_OVERSIZE
+ if (pcb->unsent == NULL) {
+ /* last unsent has been removed, reset unsent_oversize */
+ pcb->unsent_oversize = 0;
+ }
+#endif /* TCP_OVERSIZE */
+
+ pcb->flags &= ~TF_NAGLEMEMERR;
+ return ERR_OK;
+}
+
+/**
+ * Called by tcp_output() to actually send a TCP segment over IP.
+ *
+ * @param seg the tcp_seg to send
+ * @param pcb the tcp_pcb for the TCP connection used to send the segment
+ * @param netif the netif used to send the segment
+ */
+static err_t
+tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb, struct netif *netif)
+{
+ err_t err;
+ u16_t len;
+ u32_t *opts;
+
+ if (seg->p->ref != 1) {
+ /* This can happen if the pbuf of this segment is still referenced by the
+ netif driver due to deferred transmission. Since this function modifies
+ p->len, we must not continue in this case. */
+ return ERR_OK;
+ }
+
+ /* The TCP header has already been constructed, but the ackno and
+ wnd fields remain. */
+ seg->tcphdr->ackno = lwip_htonl(pcb->rcv_nxt);
+
+ /* advertise our receive window size in this TCP segment */
+#if LWIP_WND_SCALE
+ if (seg->flags & TF_SEG_OPTS_WND_SCALE) {
+ /* The Window field in a SYN segment itself (the only type where we send
+ the window scale option) is never scaled. */
+ seg->tcphdr->wnd = lwip_htons(TCPWND_MIN16(pcb->rcv_ann_wnd));
+ } else
+#endif /* LWIP_WND_SCALE */
+ {
+ seg->tcphdr->wnd = lwip_htons(TCPWND_MIN16(RCV_WND_SCALE(pcb, pcb->rcv_ann_wnd)));
+ }
+
+ pcb->rcv_ann_right_edge = pcb->rcv_nxt + pcb->rcv_ann_wnd;
+
+ /* Add any requested options. NB MSS option is only set on SYN
+ packets, so ignore it here */
+ /* cast through void* to get rid of alignment warnings */
+ opts = (u32_t *)(void *)(seg->tcphdr + 1);
+ if (seg->flags & TF_SEG_OPTS_MSS) {
+ u16_t mss;
+#if TCP_CALCULATE_EFF_SEND_MSS
+ mss = tcp_eff_send_mss(TCP_MSS, &pcb->local_ip, &pcb->remote_ip);
+#else /* TCP_CALCULATE_EFF_SEND_MSS */
+ mss = TCP_MSS;
+#endif /* TCP_CALCULATE_EFF_SEND_MSS */
+ *opts = TCP_BUILD_MSS_OPTION(mss);
+ opts += 1;
+ }
+#if LWIP_TCP_TIMESTAMPS
+ pcb->ts_lastacksent = pcb->rcv_nxt;
+
+ if (seg->flags & TF_SEG_OPTS_TS) {
+ tcp_build_timestamp_option(pcb, opts);
+ opts += 3;
+ }
+#endif
+#if LWIP_WND_SCALE
+ if (seg->flags & TF_SEG_OPTS_WND_SCALE) {
+ tcp_build_wnd_scale_option(opts);
+ opts += 1;
+ }
+#endif
+
+ /* Set retransmission timer running if it is not currently enabled
+ This must be set before checking the route. */
+ if (pcb->rtime < 0) {
+ pcb->rtime = 0;
+ }
+
+ if (pcb->rttest == 0) {
+ pcb->rttest = tcp_ticks;
+ pcb->rtseq = lwip_ntohl(seg->tcphdr->seqno);
+
+ LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_output_segment: rtseq %"U32_F"\n", pcb->rtseq));
+ }
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output_segment: %"U32_F":%"U32_F"\n",
+ lwip_htonl(seg->tcphdr->seqno), lwip_htonl(seg->tcphdr->seqno) +
+ seg->len));
+
+ len = (u16_t)((u8_t *)seg->tcphdr - (u8_t *)seg->p->payload);
+ if (len == 0) {
+ /** Exclude retransmitted segments from this count. */
+ MIB2_STATS_INC(mib2.tcpoutsegs);
+ }
+
+ seg->p->len -= len;
+ seg->p->tot_len -= len;
+
+ seg->p->payload = seg->tcphdr;
+
+ seg->tcphdr->chksum = 0;
+#if CHECKSUM_GEN_TCP
+ IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_TCP) {
+#if TCP_CHECKSUM_ON_COPY
+ u32_t acc;
+#if TCP_CHECKSUM_ON_COPY_SANITY_CHECK
+ u16_t chksum_slow = ip_chksum_pseudo(seg->p, IP_PROTO_TCP,
+ seg->p->tot_len, &pcb->local_ip, &pcb->remote_ip);
+#endif /* TCP_CHECKSUM_ON_COPY_SANITY_CHECK */
+ if ((seg->flags & TF_SEG_DATA_CHECKSUMMED) == 0) {
+ LWIP_ASSERT("data included but not checksummed",
+ seg->p->tot_len == (TCPH_HDRLEN(seg->tcphdr) * 4));
+ }
+
+ /* rebuild TCP header checksum (TCP header changes for retransmissions!) */
+ acc = ip_chksum_pseudo_partial(seg->p, IP_PROTO_TCP,
+ seg->p->tot_len, TCPH_HDRLEN(seg->tcphdr) * 4, &pcb->local_ip, &pcb->remote_ip);
+ /* add payload checksum */
+ if (seg->chksum_swapped) {
+ seg->chksum = SWAP_BYTES_IN_WORD(seg->chksum);
+ seg->chksum_swapped = 0;
+ }
+ acc += (u16_t)~(seg->chksum);
+ seg->tcphdr->chksum = FOLD_U32T(acc);
+#if TCP_CHECKSUM_ON_COPY_SANITY_CHECK
+ if (chksum_slow != seg->tcphdr->chksum) {
+ TCP_CHECKSUM_ON_COPY_SANITY_CHECK_FAIL(
+ ("tcp_output_segment: calculated checksum is %"X16_F" instead of %"X16_F"\n",
+ seg->tcphdr->chksum, chksum_slow));
+ seg->tcphdr->chksum = chksum_slow;
+ }
+#endif /* TCP_CHECKSUM_ON_COPY_SANITY_CHECK */
+#else /* TCP_CHECKSUM_ON_COPY */
+ seg->tcphdr->chksum = ip_chksum_pseudo(seg->p, IP_PROTO_TCP,
+ seg->p->tot_len, &pcb->local_ip, &pcb->remote_ip);
+#endif /* TCP_CHECKSUM_ON_COPY */
+ }
+#endif /* CHECKSUM_GEN_TCP */
+ TCP_STATS_INC(tcp.xmit);
+
+ NETIF_SET_HWADDRHINT(netif, &(pcb->addr_hint));
+ err = ip_output_if(seg->p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl,
+ pcb->tos, IP_PROTO_TCP, netif);
+ NETIF_SET_HWADDRHINT(netif, NULL);
+ return err;
+}
+
+/**
+ * Send a TCP RESET packet (empty segment with RST flag set) either to
+ * abort a connection or to show that there is no matching local connection
+ * for a received segment.
+ *
+ * Called by tcp_abort() (to abort a local connection), tcp_input() (if no
+ * matching local pcb was found), tcp_listen_input() (if incoming segment
+ * has ACK flag set) and tcp_process() (received segment in the wrong state)
+ *
+ * Since a RST segment is in most cases not sent for an active connection,
+ * tcp_rst() has a number of arguments that are taken from a tcp_pcb for
+ * most other segment output functions.
+ *
+ * @param seqno the sequence number to use for the outgoing segment
+ * @param ackno the acknowledge number to use for the outgoing segment
+ * @param local_ip the local IP address to send the segment from
+ * @param remote_ip the remote IP address to send the segment to
+ * @param local_port the local TCP port to send the segment from
+ * @param remote_port the remote TCP port to send the segment to
+ */
+void
+tcp_rst(u32_t seqno, u32_t ackno,
+ const ip_addr_t *local_ip, const ip_addr_t *remote_ip,
+ u16_t local_port, u16_t remote_port)
+{
+ struct pbuf *p;
+ struct tcp_hdr *tcphdr;
+ struct netif *netif;
+ p = pbuf_alloc(PBUF_IP, TCP_HLEN, PBUF_RAM);
+ if (p == NULL) {
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_rst: could not allocate memory for pbuf\n"));
+ return;
+ }
+ LWIP_ASSERT("check that first pbuf can hold struct tcp_hdr",
+ (p->len >= sizeof(struct tcp_hdr)));
+
+ tcphdr = (struct tcp_hdr *)p->payload;
+ tcphdr->src = lwip_htons(local_port);
+ tcphdr->dest = lwip_htons(remote_port);
+ tcphdr->seqno = lwip_htonl(seqno);
+ tcphdr->ackno = lwip_htonl(ackno);
+ TCPH_HDRLEN_FLAGS_SET(tcphdr, TCP_HLEN/4, TCP_RST | TCP_ACK);
+#if LWIP_WND_SCALE
+ tcphdr->wnd = PP_HTONS(((TCP_WND >> TCP_RCV_SCALE) & 0xFFFF));
+#else
+ tcphdr->wnd = PP_HTONS(TCP_WND);
+#endif
+ tcphdr->chksum = 0;
+ tcphdr->urgp = 0;
+
+ TCP_STATS_INC(tcp.xmit);
+ MIB2_STATS_INC(mib2.tcpoutrsts);
+
+ netif = ip_route(local_ip, remote_ip);
+ if (netif != NULL) {
+#if CHECKSUM_GEN_TCP
+ IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_TCP) {
+ tcphdr->chksum = ip_chksum_pseudo(p, IP_PROTO_TCP, p->tot_len,
+ local_ip, remote_ip);
+ }
+#endif
+ /* Send output with hardcoded TTL/HL since we have no access to the pcb */
+ ip_output_if(p, local_ip, remote_ip, TCP_TTL, 0, IP_PROTO_TCP, netif);
+ }
+ pbuf_free(p);
+ LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_rst: seqno %"U32_F" ackno %"U32_F".\n", seqno, ackno));
+}
+
+/**
+ * Requeue all unacked segments for retransmission
+ *
+ * Called by tcp_slowtmr() for slow retransmission.
+ *
+ * @param pcb the tcp_pcb for which to re-enqueue all unacked segments
+ */
+void
+tcp_rexmit_rto(struct tcp_pcb *pcb)
+{
+ struct tcp_seg *seg;
+
+ if (pcb->unacked == NULL) {
+ return;
+ }
+
+ /* Move all unacked segments to the head of the unsent queue */
+ for (seg = pcb->unacked; seg->next != NULL; seg = seg->next);
+ /* concatenate unsent queue after unacked queue */
+ seg->next = pcb->unsent;
+#if TCP_OVERSIZE_DBGCHECK
+ /* if last unsent changed, we need to update unsent_oversize */
+ if (pcb->unsent == NULL) {
+ pcb->unsent_oversize = seg->oversize_left;
+ }
+#endif /* TCP_OVERSIZE_DBGCHECK */
+ /* unsent queue is the concatenated queue (of unacked, unsent) */
+ pcb->unsent = pcb->unacked;
+ /* unacked queue is now empty */
+ pcb->unacked = NULL;
+
+ /* increment number of retransmissions */
+ if (pcb->nrtx < 0xFF) {
+ ++pcb->nrtx;
+ }
+
+ /* Don't take any RTT measurements after retransmitting. */
+ pcb->rttest = 0;
+
+ /* Do the actual retransmission */
+ tcp_output(pcb);
+}
+
+/**
+ * Requeue the first unacked segment for retransmission
+ *
+ * Called by tcp_receive() for fast retransmit.
+ *
+ * @param pcb the tcp_pcb for which to retransmit the first unacked segment
+ */
+void
+tcp_rexmit(struct tcp_pcb *pcb)
+{
+ struct tcp_seg *seg;
+ struct tcp_seg **cur_seg;
+
+ if (pcb->unacked == NULL) {
+ return;
+ }
+
+ /* Move the first unacked segment to the unsent queue */
+ /* Keep the unsent queue sorted. */
+ seg = pcb->unacked;
+ pcb->unacked = seg->next;
+
+ cur_seg = &(pcb->unsent);
+ while (*cur_seg &&
+ TCP_SEQ_LT(lwip_ntohl((*cur_seg)->tcphdr->seqno), lwip_ntohl(seg->tcphdr->seqno))) {
+ cur_seg = &((*cur_seg)->next );
+ }
+ seg->next = *cur_seg;
+ *cur_seg = seg;
+#if TCP_OVERSIZE
+ if (seg->next == NULL) {
+ /* the retransmitted segment is last in unsent, so reset unsent_oversize */
+ pcb->unsent_oversize = 0;
+ }
+#endif /* TCP_OVERSIZE */
+
+ if (pcb->nrtx < 0xFF) {
+ ++pcb->nrtx;
+ }
+
+ /* Don't take any rtt measurements after retransmitting. */
+ pcb->rttest = 0;
+
+ /* Do the actual retransmission. */
+ MIB2_STATS_INC(mib2.tcpretranssegs);
+ /* No need to call tcp_output: we are always called from tcp_input()
+ and thus tcp_output directly returns. */
+}
+
+
+/**
+ * Handle retransmission after three dupacks received
+ *
+ * @param pcb the tcp_pcb for which to retransmit the first unacked segment
+ */
+void
+tcp_rexmit_fast(struct tcp_pcb *pcb)
+{
+ if (pcb->unacked != NULL && !(pcb->flags & TF_INFR)) {
+ /* This is fast retransmit. Retransmit the first unacked segment. */
+ LWIP_DEBUGF(TCP_FR_DEBUG,
+ ("tcp_receive: dupacks %"U16_F" (%"U32_F
+ "), fast retransmit %"U32_F"\n",
+ (u16_t)pcb->dupacks, pcb->lastack,
+ lwip_ntohl(pcb->unacked->tcphdr->seqno)));
+ tcp_rexmit(pcb);
+
+ /* Set ssthresh to half of the minimum of the current
+ * cwnd and the advertised window */
+ pcb->ssthresh = LWIP_MIN(pcb->cwnd, pcb->snd_wnd) / 2;
+
+ /* The minimum value for ssthresh should be 2 MSS */
+ if (pcb->ssthresh < (2U * pcb->mss)) {
+ LWIP_DEBUGF(TCP_FR_DEBUG,
+ ("tcp_receive: The minimum value for ssthresh %"TCPWNDSIZE_F
+ " should be min 2 mss %"U16_F"...\n",
+ pcb->ssthresh, (u16_t)(2*pcb->mss)));
+ pcb->ssthresh = 2*pcb->mss;
+ }
+
+ pcb->cwnd = pcb->ssthresh + 3 * pcb->mss;
+ pcb->flags |= TF_INFR;
+
+ /* Reset the retransmission timer to prevent immediate rto retransmissions */
+ pcb->rtime = 0;
+ }
+}
+
+
+/**
+ * Send keepalive packets to keep a connection active although
+ * no data is sent over it.
+ *
+ * Called by tcp_slowtmr()
+ *
+ * @param pcb the tcp_pcb for which to send a keepalive packet
+ */
+err_t
+tcp_keepalive(struct tcp_pcb *pcb)
+{
+ err_t err;
+ struct pbuf *p;
+ struct netif *netif;
+
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: sending KEEPALIVE probe to "));
+ ip_addr_debug_print(TCP_DEBUG, &pcb->remote_ip);
+ LWIP_DEBUGF(TCP_DEBUG, ("\n"));
+
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: tcp_ticks %"U32_F" pcb->tmr %"U32_F" pcb->keep_cnt_sent %"U16_F"\n",
+ tcp_ticks, pcb->tmr, (u16_t)pcb->keep_cnt_sent));
+
+ p = tcp_output_alloc_header(pcb, 0, 0, lwip_htonl(pcb->snd_nxt - 1));
+ if (p == NULL) {
+ LWIP_DEBUGF(TCP_DEBUG,
+ ("tcp_keepalive: could not allocate memory for pbuf\n"));
+ return ERR_MEM;
+ }
+ netif = ip_route(&pcb->local_ip, &pcb->remote_ip);
+ if (netif == NULL) {
+ err = ERR_RTE;
+ } else {
+#if CHECKSUM_GEN_TCP
+ IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_TCP) {
+ struct tcp_hdr *tcphdr = (struct tcp_hdr *)p->payload;
+ tcphdr->chksum = ip_chksum_pseudo(p, IP_PROTO_TCP, p->tot_len,
+ &pcb->local_ip, &pcb->remote_ip);
+ }
+#endif /* CHECKSUM_GEN_TCP */
+ TCP_STATS_INC(tcp.xmit);
+
+ /* Send output to IP */
+ NETIF_SET_HWADDRHINT(netif, &(pcb->addr_hint));
+ err = ip_output_if(p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, 0, IP_PROTO_TCP, netif);
+ NETIF_SET_HWADDRHINT(netif, NULL);
+ }
+ pbuf_free(p);
+
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: seqno %"U32_F" ackno %"U32_F" err %d.\n",
+ pcb->snd_nxt - 1, pcb->rcv_nxt, (int)err));
+ return err;
+}
+
+
+/**
+ * Send persist timer zero-window probes to keep a connection active
+ * when a window update is lost.
+ *
+ * Called by tcp_slowtmr()
+ *
+ * @param pcb the tcp_pcb for which to send a zero-window probe packet
+ */
+err_t
+tcp_zero_window_probe(struct tcp_pcb *pcb)
+{
+ err_t err;
+ struct pbuf *p;
+ struct tcp_hdr *tcphdr;
+ struct tcp_seg *seg;
+ u16_t len;
+ u8_t is_fin;
+ u32_t snd_nxt;
+ struct netif *netif;
+
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_zero_window_probe: sending ZERO WINDOW probe to "));
+ ip_addr_debug_print(TCP_DEBUG, &pcb->remote_ip);
+ LWIP_DEBUGF(TCP_DEBUG, ("\n"));
+
+ LWIP_DEBUGF(TCP_DEBUG,
+ ("tcp_zero_window_probe: tcp_ticks %"U32_F
+ " pcb->tmr %"U32_F" pcb->keep_cnt_sent %"U16_F"\n",
+ tcp_ticks, pcb->tmr, (u16_t)pcb->keep_cnt_sent));
+
+ seg = pcb->unacked;
+
+ if (seg == NULL) {
+ seg = pcb->unsent;
+ }
+ if (seg == NULL) {
+ /* nothing to send, zero window probe not needed */
+ return ERR_OK;
+ }
+
+ is_fin = ((TCPH_FLAGS(seg->tcphdr) & TCP_FIN) != 0) && (seg->len == 0);
+ /* we want to send one seqno: either FIN or data (no options) */
+ len = is_fin ? 0 : 1;
+
+ p = tcp_output_alloc_header(pcb, 0, len, seg->tcphdr->seqno);
+ if (p == NULL) {
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_zero_window_probe: no memory for pbuf\n"));
+ return ERR_MEM;
+ }
+ tcphdr = (struct tcp_hdr *)p->payload;
+
+ if (is_fin) {
+ /* FIN segment, no data */
+ TCPH_FLAGS_SET(tcphdr, TCP_ACK | TCP_FIN);
+ } else {
+ /* Data segment, copy in one byte from the head of the unacked queue */
+ char *d = ((char *)p->payload + TCP_HLEN);
+ /* Depending on whether the segment has already been sent (unacked) or not
+ (unsent), seg->p->payload points to the IP header or TCP header.
+ Ensure we copy the first TCP data byte: */
+ pbuf_copy_partial(seg->p, d, 1, seg->p->tot_len - seg->len);
+ }
+
+ /* The byte may be acknowledged without the window being opened. */
+ snd_nxt = lwip_ntohl(seg->tcphdr->seqno) + 1;
+ if (TCP_SEQ_LT(pcb->snd_nxt, snd_nxt)) {
+ pcb->snd_nxt = snd_nxt;
+ }
+
+ netif = ip_route(&pcb->local_ip, &pcb->remote_ip);
+ if (netif == NULL) {
+ err = ERR_RTE;
+ } else {
+#if CHECKSUM_GEN_TCP
+ IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_TCP) {
+ tcphdr->chksum = ip_chksum_pseudo(p, IP_PROTO_TCP, p->tot_len,
+ &pcb->local_ip, &pcb->remote_ip);
+ }
+#endif
+ TCP_STATS_INC(tcp.xmit);
+
+ /* Send output to IP */
+ NETIF_SET_HWADDRHINT(netif, &(pcb->addr_hint));
+ err = ip_output_if(p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl,
+ 0, IP_PROTO_TCP, netif);
+ NETIF_SET_HWADDRHINT(netif, NULL);
+ }
+
+ pbuf_free(p);
+
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_zero_window_probe: seqno %"U32_F
+ " ackno %"U32_F" err %d.\n",
+ pcb->snd_nxt - 1, pcb->rcv_nxt, (int)err));
+ return err;
+}
+#endif /* LWIP_TCP */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/timeouts.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/timeouts.c
new file mode 100644
index 0000000..227d71f
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/timeouts.c
@@ -0,0 +1,433 @@
+/**
+ * @file
+ * Stack-internal timers implementation.
+ * This file includes timer callbacks for stack-internal timers as well as
+ * functions to set up or stop timers and check for expired timers.
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ * Simon Goldschmidt
+ *
+ */
+
+#include "lwip/opt.h"
+
+#include "lwip/timeouts.h"
+#include "lwip/priv/tcp_priv.h"
+
+#include "lwip/def.h"
+#include "lwip/memp.h"
+#include "lwip/priv/tcpip_priv.h"
+
+#include "lwip/ip4_frag.h"
+#include "lwip/etharp.h"
+#include "lwip/dhcp.h"
+#include "lwip/autoip.h"
+#include "lwip/igmp.h"
+#include "lwip/dns.h"
+#include "lwip/nd6.h"
+#include "lwip/ip6_frag.h"
+#include "lwip/mld6.h"
+#include "lwip/sys.h"
+#include "lwip/pbuf.h"
+
+#if LWIP_DEBUG_TIMERNAMES
+#define HANDLER(x) x, #x
+#else /* LWIP_DEBUG_TIMERNAMES */
+#define HANDLER(x) x
+#endif /* LWIP_DEBUG_TIMERNAMES */
+
+/** This array contains all stack-internal cyclic timers. To get the number of
+ * timers, use LWIP_ARRAYSIZE() */
+const struct lwip_cyclic_timer lwip_cyclic_timers[] = {
+#if LWIP_TCP
+ /* The TCP timer is a special case: it does not have to run always and
+ is triggered to start from TCP using tcp_timer_needed() */
+ {TCP_TMR_INTERVAL, HANDLER(tcp_tmr)},
+#endif /* LWIP_TCP */
+#if LWIP_IPV4
+#if IP_REASSEMBLY
+ {IP_TMR_INTERVAL, HANDLER(ip_reass_tmr)},
+#endif /* IP_REASSEMBLY */
+#if LWIP_ARP
+ {ARP_TMR_INTERVAL, HANDLER(etharp_tmr)},
+#endif /* LWIP_ARP */
+#if LWIP_DHCP
+ {DHCP_COARSE_TIMER_MSECS, HANDLER(dhcp_coarse_tmr)},
+ {DHCP_FINE_TIMER_MSECS, HANDLER(dhcp_fine_tmr)},
+#endif /* LWIP_DHCP */
+#if LWIP_AUTOIP
+ {AUTOIP_TMR_INTERVAL, HANDLER(autoip_tmr)},
+#endif /* LWIP_AUTOIP */
+#if LWIP_IGMP
+ {IGMP_TMR_INTERVAL, HANDLER(igmp_tmr)},
+#endif /* LWIP_IGMP */
+#endif /* LWIP_IPV4 */
+#if LWIP_DNS
+ {DNS_TMR_INTERVAL, HANDLER(dns_tmr)},
+#endif /* LWIP_DNS */
+#if LWIP_IPV6
+ {ND6_TMR_INTERVAL, HANDLER(nd6_tmr)},
+#if LWIP_IPV6_REASS
+ {IP6_REASS_TMR_INTERVAL, HANDLER(ip6_reass_tmr)},
+#endif /* LWIP_IPV6_REASS */
+#if LWIP_IPV6_MLD
+ {MLD6_TMR_INTERVAL, HANDLER(mld6_tmr)},
+#endif /* LWIP_IPV6_MLD */
+#endif /* LWIP_IPV6 */
+};
+
+#if LWIP_TIMERS && !LWIP_TIMERS_CUSTOM
+
+/** The one and only timeout list */
+static struct sys_timeo *next_timeout;
+static u32_t timeouts_last_time;
+
+#if LWIP_TCP
+/** global variable that shows if the tcp timer is currently scheduled or not */
+static int tcpip_tcp_timer_active;
+
+/**
+ * Timer callback function that calls tcp_tmr() and reschedules itself.
+ *
+ * @param arg unused argument
+ */
+static void
+tcpip_tcp_timer(void *arg)
+{
+ LWIP_UNUSED_ARG(arg);
+
+ /* call TCP timer handler */
+ tcp_tmr();
+ /* timer still needed? */
+ if (tcp_active_pcbs || tcp_tw_pcbs) {
+ /* restart timer */
+ sys_timeout(TCP_TMR_INTERVAL, tcpip_tcp_timer, NULL);
+ } else {
+ /* disable timer */
+ tcpip_tcp_timer_active = 0;
+ }
+}
+
+/**
+ * Called from TCP_REG when registering a new PCB:
+ * the reason is to have the TCP timer only running when
+ * there are active (or time-wait) PCBs.
+ */
+void
+tcp_timer_needed(void)
+{
+ /* timer is off but needed again? */
+ if (!tcpip_tcp_timer_active && (tcp_active_pcbs || tcp_tw_pcbs)) {
+ /* enable and start timer */
+ tcpip_tcp_timer_active = 1;
+ sys_timeout(TCP_TMR_INTERVAL, tcpip_tcp_timer, NULL);
+ }
+}
+#endif /* LWIP_TCP */
+
+/**
+ * Timer callback function that calls mld6_tmr() and reschedules itself.
+ *
+ * @param arg unused argument
+ */
+static void
+cyclic_timer(void *arg)
+{
+ const struct lwip_cyclic_timer* cyclic = (const struct lwip_cyclic_timer*)arg;
+#if LWIP_DEBUG_TIMERNAMES
+ LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: %s()\n", cyclic->handler_name));
+#endif
+ cyclic->handler();
+ sys_timeout(cyclic->interval_ms, cyclic_timer, arg);
+}
+
+/** Initialize this module */
+void sys_timeouts_init(void)
+{
+ size_t i;
+ /* tcp_tmr() at index 0 is started on demand */
+ for (i = 1; i < LWIP_ARRAYSIZE(lwip_cyclic_timers); i++) {
+ /* we have to cast via size_t to get rid of const warning
+ (this is OK as cyclic_timer() casts back to const* */
+ sys_timeout(lwip_cyclic_timers[i].interval_ms, cyclic_timer, LWIP_CONST_CAST(void*, &lwip_cyclic_timers[i]));
+ }
+
+ /* Initialise timestamp for sys_check_timeouts */
+ timeouts_last_time = sys_now();
+}
+
+/**
+ * Create a one-shot timer (aka timeout). Timeouts are processed in the
+ * following cases:
+ * - while waiting for a message using sys_timeouts_mbox_fetch()
+ * - by calling sys_check_timeouts() (NO_SYS==1 only)
+ *
+ * @param msecs time in milliseconds after that the timer should expire
+ * @param handler callback function to call when msecs have elapsed
+ * @param arg argument to pass to the callback function
+ */
+#if LWIP_DEBUG_TIMERNAMES
+void
+sys_timeout_debug(u32_t msecs, sys_timeout_handler handler, void *arg, const char* handler_name)
+#else /* LWIP_DEBUG_TIMERNAMES */
+void
+sys_timeout(u32_t msecs, sys_timeout_handler handler, void *arg)
+#endif /* LWIP_DEBUG_TIMERNAMES */
+{
+ struct sys_timeo *timeout, *t;
+ u32_t now, diff;
+
+ timeout = (struct sys_timeo *)memp_malloc(MEMP_SYS_TIMEOUT);
+ if (timeout == NULL) {
+ LWIP_ASSERT("sys_timeout: timeout != NULL, pool MEMP_SYS_TIMEOUT is empty", timeout != NULL);
+ return;
+ }
+
+ now = sys_now();
+ if (next_timeout == NULL) {
+ diff = 0;
+ timeouts_last_time = now;
+ } else {
+ diff = now - timeouts_last_time;
+ }
+
+ timeout->next = NULL;
+ timeout->h = handler;
+ timeout->arg = arg;
+ timeout->time = msecs + diff;
+#if LWIP_DEBUG_TIMERNAMES
+ timeout->handler_name = handler_name;
+ LWIP_DEBUGF(TIMERS_DEBUG, ("sys_timeout: %p msecs=%"U32_F" handler=%s arg=%p\n",
+ (void *)timeout, msecs, handler_name, (void *)arg));
+#endif /* LWIP_DEBUG_TIMERNAMES */
+
+ if (next_timeout == NULL) {
+ next_timeout = timeout;
+ return;
+ }
+
+ if (next_timeout->time > msecs) {
+ next_timeout->time -= msecs;
+ timeout->next = next_timeout;
+ next_timeout = timeout;
+ } else {
+ for (t = next_timeout; t != NULL; t = t->next) {
+ timeout->time -= t->time;
+ if (t->next == NULL || t->next->time > timeout->time) {
+ if (t->next != NULL) {
+ t->next->time -= timeout->time;
+ } else if (timeout->time > msecs) {
+ /* If this is the case, 'timeouts_last_time' and 'now' differs too much.
+ This can be due to sys_check_timeouts() not being called at the right
+ times, but also when stopping in a breakpoint. Anyway, let's assume
+ this is not wanted, so add the first timer's time instead of 'diff' */
+ timeout->time = msecs + next_timeout->time;
+ }
+ timeout->next = t->next;
+ t->next = timeout;
+ break;
+ }
+ }
+ }
+}
+
+/**
+ * Go through timeout list (for this task only) and remove the first matching
+ * entry (subsequent entries remain untouched), even though the timeout has not
+ * triggered yet.
+ *
+ * @param handler callback function that would be called by the timeout
+ * @param arg callback argument that would be passed to handler
+*/
+void
+sys_untimeout(sys_timeout_handler handler, void *arg)
+{
+ struct sys_timeo *prev_t, *t;
+
+ if (next_timeout == NULL) {
+ return;
+ }
+
+ for (t = next_timeout, prev_t = NULL; t != NULL; prev_t = t, t = t->next) {
+ if ((t->h == handler) && (t->arg == arg)) {
+ /* We have a match */
+ /* Unlink from previous in list */
+ if (prev_t == NULL) {
+ next_timeout = t->next;
+ } else {
+ prev_t->next = t->next;
+ }
+ /* If not the last one, add time of this one back to next */
+ if (t->next != NULL) {
+ t->next->time += t->time;
+ }
+ memp_free(MEMP_SYS_TIMEOUT, t);
+ return;
+ }
+ }
+ return;
+}
+
+/**
+ * @ingroup lwip_nosys
+ * Handle timeouts for NO_SYS==1 (i.e. without using
+ * tcpip_thread/sys_timeouts_mbox_fetch(). Uses sys_now() to call timeout
+ * handler functions when timeouts expire.
+ *
+ * Must be called periodically from your main loop.
+ */
+#if !NO_SYS && !defined __DOXYGEN__
+static
+#endif /* !NO_SYS */
+void
+sys_check_timeouts(void)
+{
+ if (next_timeout) {
+ struct sys_timeo *tmptimeout;
+ u32_t diff;
+ sys_timeout_handler handler;
+ void *arg;
+ u8_t had_one;
+ u32_t now;
+
+ now = sys_now();
+ /* this cares for wraparounds */
+ diff = now - timeouts_last_time;
+ do {
+ PBUF_CHECK_FREE_OOSEQ();
+ had_one = 0;
+ tmptimeout = next_timeout;
+ if (tmptimeout && (tmptimeout->time <= diff)) {
+ /* timeout has expired */
+ had_one = 1;
+ timeouts_last_time += tmptimeout->time;
+ diff -= tmptimeout->time;
+ next_timeout = tmptimeout->next;
+ handler = tmptimeout->h;
+ arg = tmptimeout->arg;
+#if LWIP_DEBUG_TIMERNAMES
+ if (handler != NULL) {
+ LWIP_DEBUGF(TIMERS_DEBUG, ("sct calling h=%s arg=%p\n",
+ tmptimeout->handler_name, arg));
+ }
+#endif /* LWIP_DEBUG_TIMERNAMES */
+ memp_free(MEMP_SYS_TIMEOUT, tmptimeout);
+ if (handler != NULL) {
+#if !NO_SYS
+ /* For LWIP_TCPIP_CORE_LOCKING, lock the core before calling the
+ timeout handler function. */
+ LOCK_TCPIP_CORE();
+#endif /* !NO_SYS */
+ handler(arg);
+#if !NO_SYS
+ UNLOCK_TCPIP_CORE();
+#endif /* !NO_SYS */
+ }
+ LWIP_TCPIP_THREAD_ALIVE();
+ }
+ /* repeat until all expired timers have been called */
+ } while (had_one);
+ }
+}
+
+/** Set back the timestamp of the last call to sys_check_timeouts()
+ * This is necessary if sys_check_timeouts() hasn't been called for a long
+ * time (e.g. while saving energy) to prevent all timer functions of that
+ * period being called.
+ */
+void
+sys_restart_timeouts(void)
+{
+ timeouts_last_time = sys_now();
+}
+
+/** Return the time left before the next timeout is due. If no timeouts are
+ * enqueued, returns 0xffffffff
+ */
+#if !NO_SYS
+static
+#endif /* !NO_SYS */
+u32_t
+sys_timeouts_sleeptime(void)
+{
+ u32_t diff;
+ if (next_timeout == NULL) {
+ return 0xffffffff;
+ }
+ diff = sys_now() - timeouts_last_time;
+ if (diff > next_timeout->time) {
+ return 0;
+ } else {
+ return next_timeout->time - diff;
+ }
+}
+
+#if !NO_SYS
+
+/**
+ * Wait (forever) for a message to arrive in an mbox.
+ * While waiting, timeouts are processed.
+ *
+ * @param mbox the mbox to fetch the message from
+ * @param msg the place to store the message
+ */
+void
+sys_timeouts_mbox_fetch(sys_mbox_t *mbox, void **msg)
+{
+ u32_t sleeptime;
+
+again:
+ if (!next_timeout) {
+ sys_arch_mbox_fetch(mbox, msg, 0);
+ return;
+ }
+
+ sleeptime = sys_timeouts_sleeptime();
+ if (sleeptime == 0 || sys_arch_mbox_fetch(mbox, msg, sleeptime) == SYS_ARCH_TIMEOUT) {
+ /* If a SYS_ARCH_TIMEOUT value is returned, a timeout occurred
+ before a message could be fetched. */
+ sys_check_timeouts();
+ /* We try again to fetch a message from the mbox. */
+ goto again;
+ }
+}
+
+#endif /* NO_SYS */
+
+#else /* LWIP_TIMERS && !LWIP_TIMERS_CUSTOM */
+/* Satisfy the TCP code which calls this function */
+void
+tcp_timer_needed(void)
+{
+}
+#endif /* LWIP_TIMERS && !LWIP_TIMERS_CUSTOM */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/udp.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/udp.c
new file mode 100644
index 0000000..ce2e3d2
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/core/udp.c
@@ -0,0 +1,1191 @@
+/**
+ * @file
+ * User Datagram Protocol module\n
+ * The code for the User Datagram Protocol UDP & UDPLite (RFC 3828).\n
+ * See also @ref udp_raw
+ *
+ * @defgroup udp_raw UDP
+ * @ingroup callbackstyle_api
+ * User Datagram Protocol module\n
+ * @see @ref raw_api and @ref netconn
+ */
+
+/*
+ * 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>
+ *
+ */
+
+/* @todo Check the use of '(struct udp_pcb).chksum_len_rx'!
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_UDP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/udp.h"
+#include "lwip/def.h"
+#include "lwip/memp.h"
+#include "lwip/inet_chksum.h"
+#include "lwip/ip_addr.h"
+#include "lwip/ip6.h"
+#include "lwip/ip6_addr.h"
+#include "lwip/netif.h"
+#include "lwip/icmp.h"
+#include "lwip/icmp6.h"
+#include "lwip/stats.h"
+#include "lwip/snmp.h"
+#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) ((u16_t)(((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;
+}
+
+/** Common code to see if the current input packet matches the pcb
+ * (current input packet is accessed via ip(4/6)_current_* macros)
+ *
+ * @param pcb pcb to check
+ * @param inp network interface on which the datagram was received (only used for IPv4)
+ * @param broadcast 1 if his is an IPv4 broadcast (global or subnet-only), 0 otherwise (only used for IPv4)
+ * @return 1 on match, 0 otherwise
+ */
+static u8_t
+udp_input_local_match(struct udp_pcb *pcb, struct netif *inp, u8_t broadcast)
+{
+ LWIP_UNUSED_ARG(inp); /* in IPv6 only case */
+ LWIP_UNUSED_ARG(broadcast); /* in IPv6 only case */
+
+ /* Dual-stack: PCBs listening to any IP type also listen to any IP address */
+ if (IP_IS_ANY_TYPE_VAL(pcb->local_ip)) {
+#if LWIP_IPV4 && IP_SOF_BROADCAST_RECV
+ if ((broadcast != 0) && !ip_get_option(pcb, SOF_BROADCAST)) {
+ return 0;
+ }
+#endif /* LWIP_IPV4 && IP_SOF_BROADCAST_RECV */
+ return 1;
+ }
+
+ /* Only need to check PCB if incoming IP version matches PCB IP version */
+ if (IP_ADDR_PCB_VERSION_MATCH_EXACT(pcb, ip_current_dest_addr())) {
+#if LWIP_IPV4
+ /* Special case: IPv4 broadcast: all or broadcasts in my subnet
+ * Note: broadcast variable can only be 1 if it is an IPv4 broadcast */
+ if (broadcast != 0) {
+#if IP_SOF_BROADCAST_RECV
+ if (ip_get_option(pcb, SOF_BROADCAST))
+#endif /* IP_SOF_BROADCAST_RECV */
+ {
+ if (ip4_addr_isany(ip_2_ip4(&pcb->local_ip)) ||
+ ((ip4_current_dest_addr()->addr == IPADDR_BROADCAST)) ||
+ ip4_addr_netcmp(ip_2_ip4(&pcb->local_ip), ip4_current_dest_addr(), netif_ip4_netmask(inp))) {
+ return 1;
+ }
+ }
+ } else
+#endif /* LWIP_IPV4 */
+ /* Handle IPv4 and IPv6: all or exact match */
+ if (ip_addr_isany(&pcb->local_ip) || ip_addr_cmp(&pcb->local_ip, ip_current_dest_addr())) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Process an incoming UDP datagram.
+ *
+ * Given an incoming UDP datagram (as a chain of pbufs) this function
+ * finds a corresponding UDP PCB and hands over the pbuf to the pcbs
+ * recv function. If no pcb is found or the datagram is incorrect, the
+ * pbuf is freed.
+ *
+ * @param p pbuf to be demultiplexed to a UDP PCB (p->payload pointing to the UDP header)
+ * @param inp network interface on which the datagram was received.
+ *
+ */
+void
+udp_input(struct pbuf *p, struct netif *inp)
+{
+ struct udp_hdr *udphdr;
+ struct udp_pcb *pcb, *prev;
+ struct udp_pcb *uncon_pcb;
+ u16_t src, dest;
+ u8_t broadcast;
+ u8_t for_us = 0;
+
+ LWIP_UNUSED_ARG(inp);
+
+ PERF_START;
+
+ UDP_STATS_INC(udp.recv);
+
+ /* Check minimum length (UDP header) */
+ if (p->len < UDP_HLEN) {
+ /* drop short packets */
+ LWIP_DEBUGF(UDP_DEBUG,
+ ("udp_input: short UDP datagram (%"U16_F" bytes) discarded\n", p->tot_len));
+ UDP_STATS_INC(udp.lenerr);
+ UDP_STATS_INC(udp.drop);
+ MIB2_STATS_INC(mib2.udpinerrors);
+ pbuf_free(p);
+ goto end;
+ }
+
+ udphdr = (struct udp_hdr *)p->payload;
+
+ /* is broadcast packet ? */
+ broadcast = ip_addr_isbroadcast(ip_current_dest_addr(), ip_current_netif());
+
+ LWIP_DEBUGF(UDP_DEBUG, ("udp_input: received datagram of length %"U16_F"\n", p->tot_len));
+
+ /* convert src and dest ports to host byte order */
+ src = lwip_ntohs(udphdr->src);
+ dest = lwip_ntohs(udphdr->dest);
+
+ udp_debug_print(udphdr);
+
+ /* print the UDP source and destination */
+ LWIP_DEBUGF(UDP_DEBUG, ("udp ("));
+ ip_addr_debug_print(UDP_DEBUG, ip_current_dest_addr());
+ LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F") <-- (", lwip_ntohs(udphdr->dest)));
+ ip_addr_debug_print(UDP_DEBUG, ip_current_src_addr());
+ LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F")\n", lwip_ntohs(udphdr->src)));
+
+ pcb = NULL;
+ prev = NULL;
+ uncon_pcb = NULL;
+ /* Iterate through the UDP pcb list for a matching pcb.
+ * 'Perfect match' pcbs (connected to the remote port & ip address) are
+ * preferred. If no perfect match is found, the first unconnected pcb that
+ * matches the local port and ip address gets the datagram. */
+ for (pcb = udp_pcbs; pcb != NULL; pcb = pcb->next) {
+ /* print the PCB local and remote address */
+ LWIP_DEBUGF(UDP_DEBUG, ("pcb ("));
+ ip_addr_debug_print(UDP_DEBUG, &pcb->local_ip);
+ LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F") <-- (", pcb->local_port));
+ ip_addr_debug_print(UDP_DEBUG, &pcb->remote_ip);
+ LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F")\n", pcb->remote_port));
+
+ /* compare PCB local addr+port to UDP destination addr+port */
+ if ((pcb->local_port == dest) &&
+ (udp_input_local_match(pcb, inp, broadcast) != 0)) {
+ if (((pcb->flags & UDP_FLAGS_CONNECTED) == 0) &&
+ ((uncon_pcb == NULL)
+#if SO_REUSE
+ /* prefer specific IPs over cath-all */
+ || !ip_addr_isany(&pcb->local_ip)
+#endif /* SO_REUSE */
+ )) {
+ /* the first unconnected matching PCB */
+ uncon_pcb = pcb;
+ }
+
+ /* compare PCB remote addr+port to UDP source addr+port */
+ if ((pcb->remote_port == src) &&
+ (ip_addr_isany_val(pcb->remote_ip) ||
+ ip_addr_cmp(&pcb->remote_ip, ip_current_src_addr()))) {
+ /* the first fully matching PCB */
+ if (prev != NULL) {
+ /* move the pcb to the front of udp_pcbs so that is
+ found faster next time */
+ prev->next = pcb->next;
+ pcb->next = udp_pcbs;
+ udp_pcbs = pcb;
+ } else {
+ UDP_STATS_INC(udp.cachehit);
+ }
+ break;
+ }
+ }
+
+ prev = pcb;
+ }
+ /* no fully matching pcb found? then look for an unconnected pcb */
+ if (pcb == NULL) {
+ pcb = uncon_pcb;
+ }
+
+ /* Check checksum if this is a match or if it was directed at us. */
+ if (pcb != NULL) {
+ for_us = 1;
+ } else {
+#if LWIP_IPV6
+ if (ip_current_is_v6()) {
+ for_us = netif_get_ip6_addr_match(inp, ip6_current_dest_addr()) >= 0;
+ }
+#endif /* LWIP_IPV6 */
+#if LWIP_IPV4
+ if (!ip_current_is_v6()) {
+ for_us = ip4_addr_cmp(netif_ip4_addr(inp), ip4_current_dest_addr());
+ }
+#endif /* LWIP_IPV4 */
+ }
+
+ if (for_us) {
+ LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_input: calculating checksum\n"));
+#if CHECKSUM_CHECK_UDP
+ IF__NETIF_CHECKSUM_ENABLED(inp, CHECKSUM_CHECK_UDP) {
+#if LWIP_UDPLITE
+ if (ip_current_header_proto() == IP_PROTO_UDPLITE) {
+ /* Do the UDP Lite checksum */
+ u16_t chklen = lwip_ntohs(udphdr->len);
+ if (chklen < sizeof(struct udp_hdr)) {
+ if (chklen == 0) {
+ /* For UDP-Lite, checksum length of 0 means checksum
+ over the complete packet (See RFC 3828 chap. 3.1) */
+ chklen = p->tot_len;
+ } else {
+ /* At least the UDP-Lite header must be covered by the
+ checksum! (Again, see RFC 3828 chap. 3.1) */
+ goto chkerr;
+ }
+ }
+ if (ip_chksum_pseudo_partial(p, IP_PROTO_UDPLITE,
+ p->tot_len, chklen,
+ ip_current_src_addr(), ip_current_dest_addr()) != 0) {
+ goto chkerr;
+ }
+ } else
+#endif /* LWIP_UDPLITE */
+ {
+ if (udphdr->chksum != 0) {
+ if (ip_chksum_pseudo(p, IP_PROTO_UDP, p->tot_len,
+ ip_current_src_addr(),
+ ip_current_dest_addr()) != 0) {
+ goto chkerr;
+ }
+ }
+ }
+ }
+#endif /* CHECKSUM_CHECK_UDP */
+ if (pbuf_header(p, -UDP_HLEN)) {
+ /* Can we cope with this failing? Just assert for now */
+ LWIP_ASSERT("pbuf_header failed\n", 0);
+ UDP_STATS_INC(udp.drop);
+ MIB2_STATS_INC(mib2.udpinerrors);
+ pbuf_free(p);
+ goto end;
+ }
+
+ if (pcb != NULL) {
+ MIB2_STATS_INC(mib2.udpindatagrams);
+#if SO_REUSE && SO_REUSE_RXTOALL
+ if (ip_get_option(pcb, SOF_REUSEADDR) &&
+ (broadcast || ip_addr_ismulticast(ip_current_dest_addr()))) {
+ /* pass broadcast- or multicast packets to all multicast pcbs
+ if SOF_REUSEADDR is set on the first match */
+ struct udp_pcb *mpcb;
+ u8_t p_header_changed = 0;
+ s16_t hdrs_len = (s16_t)(ip_current_header_tot_len() + UDP_HLEN);
+ for (mpcb = udp_pcbs; mpcb != NULL; mpcb = mpcb->next) {
+ if (mpcb != pcb) {
+ /* compare PCB local addr+port to UDP destination addr+port */
+ if ((mpcb->local_port == dest) &&
+ (udp_input_local_match(mpcb, inp, broadcast) != 0)) {
+ /* pass a copy of the packet to all local matches */
+ if (mpcb->recv != NULL) {
+ struct pbuf *q;
+ /* for that, move payload to IP header again */
+ if (p_header_changed == 0) {
+ pbuf_header_force(p, hdrs_len);
+ p_header_changed = 1;
+ }
+ q = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM);
+ if (q != NULL) {
+ err_t err = pbuf_copy(q, p);
+ if (err == ERR_OK) {
+ /* move payload to UDP data */
+ pbuf_header(q, -hdrs_len);
+ mpcb->recv(mpcb->recv_arg, mpcb, q, ip_current_src_addr(), src);
+ }
+ }
+ }
+ }
+ }
+ }
+ if (p_header_changed) {
+ /* and move payload to UDP data again */
+ pbuf_header(p, -hdrs_len);
+ }
+ }
+#endif /* SO_REUSE && SO_REUSE_RXTOALL */
+ /* callback */
+ if (pcb->recv != NULL) {
+ /* now the recv function is responsible for freeing p */
+ pcb->recv(pcb->recv_arg, pcb, p, ip_current_src_addr(), src);
+ } else {
+ /* no recv function registered? then we have to free the pbuf! */
+ pbuf_free(p);
+ goto end;
+ }
+ } else {
+ LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_input: not for us.\n"));
+
+#if LWIP_ICMP || LWIP_ICMP6
+ /* No match was found, send ICMP destination port unreachable unless
+ destination address was broadcast/multicast. */
+ if (!broadcast && !ip_addr_ismulticast(ip_current_dest_addr())) {
+ /* move payload pointer back to ip header */
+ pbuf_header_force(p, (s16_t)(ip_current_header_tot_len() + UDP_HLEN));
+ icmp_port_unreach(ip_current_is_v6(), p);
+ }
+#endif /* LWIP_ICMP || LWIP_ICMP6 */
+ UDP_STATS_INC(udp.proterr);
+ UDP_STATS_INC(udp.drop);
+ MIB2_STATS_INC(mib2.udpnoports);
+ pbuf_free(p);
+ }
+ } else {
+ pbuf_free(p);
+ }
+end:
+ PERF_STOP("udp_input");
+ return;
+#if CHECKSUM_CHECK_UDP
+chkerr:
+ LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+ ("udp_input: UDP (or UDP Lite) datagram discarded due to failing checksum\n"));
+ UDP_STATS_INC(udp.chkerr);
+ UDP_STATS_INC(udp.drop);
+ MIB2_STATS_INC(mib2.udpinerrors);
+ pbuf_free(p);
+ PERF_STOP("udp_input");
+#endif /* CHECKSUM_CHECK_UDP */
+}
+
+/**
+ * @ingroup udp_raw
+ * Send data using UDP.
+ *
+ * @param pcb UDP PCB used to send the data.
+ * @param p chain of pbuf's to be sent.
+ *
+ * The datagram will be sent to the current remote_ip & remote_port
+ * stored in pcb. If the pcb is not bound to a port, it will
+ * automatically be bound to a random port.
+ *
+ * @return lwIP error code.
+ * - ERR_OK. Successful. No error occurred.
+ * - ERR_MEM. Out of memory.
+ * - ERR_RTE. Could not find route to destination address.
+ * - ERR_VAL. No PCB or PCB is dual-stack
+ * - More errors could be returned by lower protocol layers.
+ *
+ * @see udp_disconnect() udp_sendto()
+ */
+err_t
+udp_send(struct udp_pcb *pcb, struct pbuf *p)
+{
+ if ((pcb == NULL) || IP_IS_ANY_TYPE_VAL(pcb->remote_ip)) {
+ return ERR_VAL;
+ }
+
+ /* send to the packet using remote ip and port stored in the pcb */
+ return udp_sendto(pcb, p, &pcb->remote_ip, pcb->remote_port);
+}
+
+#if LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP
+/** @ingroup udp_raw
+ * Same as udp_send() but with checksum
+ */
+err_t
+udp_send_chksum(struct udp_pcb *pcb, struct pbuf *p,
+ u8_t have_chksum, u16_t chksum)
+{
+ if ((pcb == NULL) || IP_IS_ANY_TYPE_VAL(pcb->remote_ip)) {
+ return ERR_VAL;
+ }
+
+ /* send to the packet using remote ip and port stored in the pcb */
+ return udp_sendto_chksum(pcb, p, &pcb->remote_ip, pcb->remote_port,
+ have_chksum, chksum);
+}
+#endif /* LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP */
+
+/**
+ * @ingroup udp_raw
+ * Send data to a specified address using UDP.
+ *
+ * @param pcb UDP PCB used to send the data.
+ * @param p chain of pbuf's to be sent.
+ * @param dst_ip Destination IP address.
+ * @param dst_port Destination UDP port.
+ *
+ * dst_ip & dst_port are expected to be in the same byte order as in the pcb.
+ *
+ * If the PCB already has a remote address association, it will
+ * be restored after the data is sent.
+ *
+ * @return lwIP error code (@see udp_send for possible error codes)
+ *
+ * @see udp_disconnect() udp_send()
+ */
+err_t
+udp_sendto(struct udp_pcb *pcb, struct pbuf *p,
+ const ip_addr_t *dst_ip, u16_t dst_port)
+{
+#if LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP
+ return udp_sendto_chksum(pcb, p, dst_ip, dst_port, 0, 0);
+}
+
+/** @ingroup udp_raw
+ * Same as udp_sendto(), but with checksum */
+err_t
+udp_sendto_chksum(struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *dst_ip,
+ u16_t dst_port, u8_t have_chksum, u16_t chksum)
+{
+#endif /* LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP */
+ struct netif *netif;
+ const ip_addr_t *dst_ip_route = dst_ip;
+
+ if ((pcb == NULL) || (dst_ip == NULL) || !IP_ADDR_PCB_VERSION_MATCH(pcb, dst_ip)) {
+ return ERR_VAL;
+ }
+
+ LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_send\n"));
+
+#if LWIP_IPV6 || (LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS)
+ if (ip_addr_ismulticast(dst_ip_route)) {
+#if LWIP_IPV6
+ if (IP_IS_V6(dst_ip)) {
+ /* For multicast, find a netif based on source address. */
+ dst_ip_route = &pcb->local_ip;
+ } else
+#endif /* LWIP_IPV6 */
+ {
+#if LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS
+ /* IPv4 does not use source-based routing by default, so we use an
+ administratively selected interface for multicast by default.
+ However, this can be overridden by setting an interface address
+ in pcb->multicast_ip that is used for routing. */
+ if (!ip_addr_isany_val(pcb->multicast_ip) &&
+ !ip4_addr_cmp(ip_2_ip4(&pcb->multicast_ip), IP4_ADDR_BROADCAST)) {
+ dst_ip_route = &pcb->multicast_ip;
+ }
+#endif /* LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS */
+ }
+ }
+#endif /* LWIP_IPV6 || (LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS) */
+
+ /* find the outgoing network interface for this packet */
+ if(IP_IS_ANY_TYPE_VAL(pcb->local_ip)) {
+ /* Don't call ip_route() with IP_ANY_TYPE */
+ netif = ip_route(IP46_ADDR_ANY(IP_GET_TYPE(dst_ip_route)), dst_ip_route);
+ } else {
+ netif = ip_route(&pcb->local_ip, dst_ip_route);
+ }
+
+ /* no outgoing network interface could be found? */
+ if (netif == NULL) {
+ LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("udp_send: No route to "));
+ ip_addr_debug_print(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, dst_ip);
+ LWIP_DEBUGF(UDP_DEBUG, ("\n"));
+ UDP_STATS_INC(udp.rterr);
+ return ERR_RTE;
+ }
+#if LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP
+ return udp_sendto_if_chksum(pcb, p, dst_ip, dst_port, netif, have_chksum, chksum);
+#else /* LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP */
+ return udp_sendto_if(pcb, p, dst_ip, dst_port, netif);
+#endif /* LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP */
+}
+
+/**
+ * @ingroup udp_raw
+ * Send data to a specified address using UDP.
+ * The netif used for sending can be specified.
+ *
+ * This function exists mainly for DHCP, to be able to send UDP packets
+ * on a netif that is still down.
+ *
+ * @param pcb UDP PCB used to send the data.
+ * @param p chain of pbuf's to be sent.
+ * @param dst_ip Destination IP address.
+ * @param dst_port Destination UDP port.
+ * @param netif the netif used for sending.
+ *
+ * dst_ip & dst_port are expected to be in the same byte order as in the pcb.
+ *
+ * @return lwIP error code (@see udp_send for possible error codes)
+ *
+ * @see udp_disconnect() udp_send()
+ */
+err_t
+udp_sendto_if(struct udp_pcb *pcb, struct pbuf *p,
+ const ip_addr_t *dst_ip, u16_t dst_port, struct netif *netif)
+{
+#if LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP
+ return udp_sendto_if_chksum(pcb, p, dst_ip, dst_port, netif, 0, 0);
+}
+
+/** Same as udp_sendto_if(), but with checksum */
+err_t
+udp_sendto_if_chksum(struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *dst_ip,
+ u16_t dst_port, struct netif *netif, u8_t have_chksum,
+ u16_t chksum)
+{
+#endif /* LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP */
+ const ip_addr_t *src_ip;
+
+ if ((pcb == NULL) || (dst_ip == NULL) || !IP_ADDR_PCB_VERSION_MATCH(pcb, dst_ip)) {
+ return ERR_VAL;
+ }
+
+ /* PCB local address is IP_ANY_ADDR? */
+#if LWIP_IPV6
+ if (IP_IS_V6(dst_ip)) {
+ if (ip6_addr_isany(ip_2_ip6(&pcb->local_ip))) {
+ src_ip = ip6_select_source_address(netif, ip_2_ip6(dst_ip));
+ if (src_ip == NULL) {
+ /* No suitable source address was found. */
+ return ERR_RTE;
+ }
+ } else {
+ /* use UDP PCB local IPv6 address as source address, if still valid. */
+ if (netif_get_ip6_addr_match(netif, ip_2_ip6(&pcb->local_ip)) < 0) {
+ /* Address isn't valid anymore. */
+ return ERR_RTE;
+ }
+ src_ip = &pcb->local_ip;
+ }
+ }
+#endif /* LWIP_IPV6 */
+#if LWIP_IPV4 && LWIP_IPV6
+ else
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+#if LWIP_IPV4
+ if (ip4_addr_isany(ip_2_ip4(&pcb->local_ip)) ||
+ ip4_addr_ismulticast(ip_2_ip4(&pcb->local_ip))) {
+ /* if the local_ip is any or multicast
+ * use the outgoing network interface IP address as source address */
+ src_ip = netif_ip_addr4(netif);
+ } else {
+ /* check if UDP PCB local IP address is correct
+ * this could be an old address if netif->ip_addr has changed */
+ if (!ip4_addr_cmp(ip_2_ip4(&(pcb->local_ip)), netif_ip4_addr(netif))) {
+ /* local_ip doesn't match, drop the packet */
+ return ERR_RTE;
+ }
+ /* use UDP PCB local IP address as source address */
+ src_ip = &pcb->local_ip;
+ }
+#endif /* LWIP_IPV4 */
+#if LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP
+ return udp_sendto_if_src_chksum(pcb, p, dst_ip, dst_port, netif, have_chksum, chksum, src_ip);
+#else /* LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP */
+ return udp_sendto_if_src(pcb, p, dst_ip, dst_port, netif, src_ip);
+#endif /* LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP */
+}
+
+/** @ingroup udp_raw
+ * Same as @ref udp_sendto_if, but with source address */
+err_t
+udp_sendto_if_src(struct udp_pcb *pcb, struct pbuf *p,
+ const ip_addr_t *dst_ip, u16_t dst_port, struct netif *netif, const ip_addr_t *src_ip)
+{
+#if LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP
+ return udp_sendto_if_src_chksum(pcb, p, dst_ip, dst_port, netif, 0, 0, src_ip);
+}
+
+/** Same as udp_sendto_if_src(), but with checksum */
+err_t
+udp_sendto_if_src_chksum(struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *dst_ip,
+ u16_t dst_port, struct netif *netif, u8_t have_chksum,
+ u16_t chksum, const ip_addr_t *src_ip)
+{
+#endif /* LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP */
+ struct udp_hdr *udphdr;
+ err_t err;
+ struct pbuf *q; /* q will be sent down the stack */
+ u8_t ip_proto;
+ u8_t ttl;
+
+ if ((pcb == NULL) || (dst_ip == NULL) || !IP_ADDR_PCB_VERSION_MATCH(pcb, src_ip) ||
+ !IP_ADDR_PCB_VERSION_MATCH(pcb, dst_ip)) {
+ return ERR_VAL;
+ }
+
+#if LWIP_IPV4 && IP_SOF_BROADCAST
+ /* broadcast filter? */
+ if (!ip_get_option(pcb, SOF_BROADCAST) &&
+#if LWIP_IPV6
+ IP_IS_V4(dst_ip) &&
+#endif /* LWIP_IPV6 */
+ ip_addr_isbroadcast(dst_ip, netif)) {
+ LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+ ("udp_sendto_if: SOF_BROADCAST not enabled on pcb %p\n", (void *)pcb));
+ return ERR_VAL;
+ }
+#endif /* LWIP_IPV4 && IP_SOF_BROADCAST */
+
+ /* if the PCB is not yet bound to a port, bind it here */
+ if (pcb->local_port == 0) {
+ LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_send: not yet bound to a port, binding now\n"));
+ err = udp_bind(pcb, &pcb->local_ip, pcb->local_port);
+ if (err != ERR_OK) {
+ LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("udp_send: forced port bind failed\n"));
+ return err;
+ }
+ }
+
+ /* not enough space to add an UDP header to first pbuf in given p chain? */
+ if (pbuf_header(p, UDP_HLEN)) {
+ /* allocate header in a separate new pbuf */
+ q = pbuf_alloc(PBUF_IP, UDP_HLEN, PBUF_RAM);
+ /* new header pbuf could not be allocated? */
+ if (q == NULL) {
+ LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("udp_send: could not allocate header\n"));
+ return ERR_MEM;
+ }
+ if (p->tot_len != 0) {
+ /* chain header q in front of given pbuf p (only if p contains data) */
+ pbuf_chain(q, p);
+ }
+ /* first pbuf q points to header pbuf */
+ LWIP_DEBUGF(UDP_DEBUG,
+ ("udp_send: added header pbuf %p before given pbuf %p\n", (void *)q, (void *)p));
+ } else {
+ /* adding space for header within p succeeded */
+ /* first pbuf q equals given pbuf */
+ q = p;
+ LWIP_DEBUGF(UDP_DEBUG, ("udp_send: added header in given pbuf %p\n", (void *)p));
+ }
+ LWIP_ASSERT("check that first pbuf can hold struct udp_hdr",
+ (q->len >= sizeof(struct udp_hdr)));
+ /* q now represents the packet to be sent */
+ udphdr = (struct udp_hdr *)q->payload;
+ udphdr->src = lwip_htons(pcb->local_port);
+ udphdr->dest = lwip_htons(dst_port);
+ /* in UDP, 0 checksum means 'no checksum' */
+ udphdr->chksum = 0x0000;
+
+ /* Multicast Loop? */
+#if (LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS) || (LWIP_IPV6 && LWIP_IPV6_MLD)
+ if (((pcb->flags & UDP_FLAGS_MULTICAST_LOOP) != 0) && ip_addr_ismulticast(dst_ip)) {
+ q->flags |= PBUF_FLAG_MCASTLOOP;
+ }
+#endif /* (LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS) || (LWIP_IPV6 && LWIP_IPV6_MLD) */
+
+ LWIP_DEBUGF(UDP_DEBUG, ("udp_send: sending datagram of length %"U16_F"\n", q->tot_len));
+
+#if LWIP_UDPLITE
+ /* UDP Lite protocol? */
+ if (pcb->flags & UDP_FLAGS_UDPLITE) {
+ u16_t chklen, chklen_hdr;
+ LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP LITE packet length %"U16_F"\n", q->tot_len));
+ /* set UDP message length in UDP header */
+ chklen_hdr = chklen = pcb->chksum_len_tx;
+ if ((chklen < sizeof(struct udp_hdr)) || (chklen > q->tot_len)) {
+ if (chklen != 0) {
+ LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP LITE pcb->chksum_len is illegal: %"U16_F"\n", chklen));
+ }
+ /* For UDP-Lite, checksum length of 0 means checksum
+ over the complete packet. (See RFC 3828 chap. 3.1)
+ At least the UDP-Lite header must be covered by the
+ checksum, therefore, if chksum_len has an illegal
+ value, we generate the checksum over the complete
+ packet to be safe. */
+ chklen_hdr = 0;
+ chklen = q->tot_len;
+ }
+ udphdr->len = lwip_htons(chklen_hdr);
+ /* calculate checksum */
+#if CHECKSUM_GEN_UDP
+ IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_UDP) {
+#if LWIP_CHECKSUM_ON_COPY
+ if (have_chksum) {
+ chklen = UDP_HLEN;
+ }
+#endif /* LWIP_CHECKSUM_ON_COPY */
+ udphdr->chksum = ip_chksum_pseudo_partial(q, IP_PROTO_UDPLITE,
+ q->tot_len, chklen, src_ip, dst_ip);
+#if LWIP_CHECKSUM_ON_COPY
+ if (have_chksum) {
+ u32_t acc;
+ acc = udphdr->chksum + (u16_t)~(chksum);
+ udphdr->chksum = FOLD_U32T(acc);
+ }
+#endif /* LWIP_CHECKSUM_ON_COPY */
+
+ /* chksum zero must become 0xffff, as zero means 'no checksum' */
+ if (udphdr->chksum == 0x0000) {
+ udphdr->chksum = 0xffff;
+ }
+ }
+#endif /* CHECKSUM_GEN_UDP */
+
+ ip_proto = IP_PROTO_UDPLITE;
+ } else
+#endif /* LWIP_UDPLITE */
+ { /* UDP */
+ LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP packet length %"U16_F"\n", q->tot_len));
+ udphdr->len = lwip_htons(q->tot_len);
+ /* calculate checksum */
+#if CHECKSUM_GEN_UDP
+ IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_UDP) {
+ /* Checksum is mandatory over IPv6. */
+ if (IP_IS_V6(dst_ip) || (pcb->flags & UDP_FLAGS_NOCHKSUM) == 0) {
+ u16_t udpchksum;
+#if LWIP_CHECKSUM_ON_COPY
+ if (have_chksum) {
+ u32_t acc;
+ udpchksum = ip_chksum_pseudo_partial(q, IP_PROTO_UDP,
+ q->tot_len, UDP_HLEN, src_ip, dst_ip);
+ acc = udpchksum + (u16_t)~(chksum);
+ udpchksum = FOLD_U32T(acc);
+ } else
+#endif /* LWIP_CHECKSUM_ON_COPY */
+ {
+ udpchksum = ip_chksum_pseudo(q, IP_PROTO_UDP, q->tot_len,
+ src_ip, dst_ip);
+ }
+
+ /* chksum zero must become 0xffff, as zero means 'no checksum' */
+ if (udpchksum == 0x0000) {
+ udpchksum = 0xffff;
+ }
+ udphdr->chksum = udpchksum;
+ }
+ }
+#endif /* CHECKSUM_GEN_UDP */
+ ip_proto = IP_PROTO_UDP;
+ }
+
+ /* Determine TTL to use */
+#if LWIP_MULTICAST_TX_OPTIONS
+ ttl = (ip_addr_ismulticast(dst_ip) ? udp_get_multicast_ttl(pcb) : pcb->ttl);
+#else /* LWIP_MULTICAST_TX_OPTIONS */
+ ttl = pcb->ttl;
+#endif /* LWIP_MULTICAST_TX_OPTIONS */
+
+ LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP checksum 0x%04"X16_F"\n", udphdr->chksum));
+ LWIP_DEBUGF(UDP_DEBUG, ("udp_send: ip_output_if (,,,,0x%02"X16_F",)\n", (u16_t)ip_proto));
+ /* output to IP */
+ NETIF_SET_HWADDRHINT(netif, &(pcb->addr_hint));
+ err = ip_output_if_src(q, src_ip, dst_ip, ttl, pcb->tos, ip_proto, netif);
+ NETIF_SET_HWADDRHINT(netif, NULL);
+
+ /* @todo: must this be increased even if error occurred? */
+ MIB2_STATS_INC(mib2.udpoutdatagrams);
+
+ /* did we chain a separate header pbuf earlier? */
+ if (q != p) {
+ /* free the header pbuf */
+ pbuf_free(q);
+ q = NULL;
+ /* p is still referenced by the caller, and will live on */
+ }
+
+ UDP_STATS_INC(udp.xmit);
+ return err;
+}
+
+/**
+ * @ingroup udp_raw
+ * Bind an UDP PCB.
+ *
+ * @param pcb UDP PCB to be bound with a local address ipaddr and port.
+ * @param ipaddr local IP address to bind with. Use IP4_ADDR_ANY to
+ * bind to all local interfaces.
+ * @param port local UDP port to bind with. Use 0 to automatically bind
+ * to a random port between UDP_LOCAL_PORT_RANGE_START and
+ * UDP_LOCAL_PORT_RANGE_END.
+ *
+ * ipaddr & port are expected to be in the same byte order as in the pcb.
+ *
+ * @return lwIP error code.
+ * - ERR_OK. Successful. No error occurred.
+ * - ERR_USE. The specified ipaddr and port are already bound to by
+ * another UDP PCB.
+ *
+ * @see udp_disconnect()
+ */
+err_t
+udp_bind(struct udp_pcb *pcb, const ip_addr_t *ipaddr, u16_t port)
+{
+ struct udp_pcb *ipcb;
+ u8_t rebind;
+
+#if LWIP_IPV4
+ /* Don't propagate NULL pointer (IPv4 ANY) to subsequent functions */
+ if (ipaddr == NULL) {
+ ipaddr = IP4_ADDR_ANY;
+ }
+#endif /* LWIP_IPV4 */
+
+ /* still need to check for ipaddr == NULL in IPv6 only case */
+ if ((pcb == NULL) || (ipaddr == NULL)) {
+ return ERR_VAL;
+ }
+
+ LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_bind(ipaddr = "));
+ ip_addr_debug_print(UDP_DEBUG | LWIP_DBG_TRACE, ipaddr);
+ LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, (", port = %"U16_F")\n", port));
+
+ rebind = 0;
+ /* Check for double bind and rebind of the same pcb */
+ for (ipcb = udp_pcbs; ipcb != NULL; ipcb = ipcb->next) {
+ /* is this UDP PCB already on active list? */
+ if (pcb == ipcb) {
+ rebind = 1;
+ break;
+ }
+ }
+
+ /* no port specified? */
+ if (port == 0) {
+ port = udp_new_port();
+ if (port == 0) {
+ /* no more ports available in local range */
+ LWIP_DEBUGF(UDP_DEBUG, ("udp_bind: out of free UDP ports\n"));
+ return ERR_USE;
+ }
+ } else {
+ for (ipcb = udp_pcbs; ipcb != NULL; ipcb = ipcb->next) {
+ if (pcb != ipcb) {
+ /* By default, we don't allow to bind to a port that any other udp
+ PCB is already bound to, unless *all* PCBs with that port have tha
+ REUSEADDR flag set. */
+#if SO_REUSE
+ if (!ip_get_option(pcb, SOF_REUSEADDR) ||
+ !ip_get_option(ipcb, SOF_REUSEADDR))
+#endif /* SO_REUSE */
+ {
+ /* port matches that of PCB in list and REUSEADDR not set -> reject */
+ if ((ipcb->local_port == port) &&
+ /* IP address matches? */
+ ip_addr_cmp(&ipcb->local_ip, ipaddr)) {
+ /* other PCB already binds to this local IP and port */
+ LWIP_DEBUGF(UDP_DEBUG,
+ ("udp_bind: local port %"U16_F" already bound by another pcb\n", port));
+ return ERR_USE;
+ }
+ }
+ }
+ }
+ }
+
+ ip_addr_set_ipaddr(&pcb->local_ip, ipaddr);
+
+ pcb->local_port = port;
+ mib2_udp_bind(pcb);
+ /* pcb not active yet? */
+ if (rebind == 0) {
+ /* place the PCB on the active list if not already there */
+ pcb->next = udp_pcbs;
+ udp_pcbs = pcb;
+ }
+ LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("udp_bind: bound to "));
+ ip_addr_debug_print(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, &pcb->local_ip);
+ LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, (", port %"U16_F")\n", pcb->local_port));
+ return ERR_OK;
+}
+
+/**
+ * @ingroup udp_raw
+ * Connect an UDP PCB.
+ *
+ * This will associate the UDP PCB with the remote address.
+ *
+ * @param pcb UDP PCB to be connected with remote address ipaddr and port.
+ * @param ipaddr remote IP address to connect with.
+ * @param port remote UDP port to connect with.
+ *
+ * @return lwIP error code
+ *
+ * ipaddr & port are expected to be in the same byte order as in the pcb.
+ *
+ * The udp pcb is bound to a random local port if not already bound.
+ *
+ * @see udp_disconnect()
+ */
+err_t
+udp_connect(struct udp_pcb *pcb, const ip_addr_t *ipaddr, u16_t port)
+{
+ struct udp_pcb *ipcb;
+
+ if ((pcb == NULL) || (ipaddr == NULL)) {
+ return ERR_VAL;
+ }
+
+ if (pcb->local_port == 0) {
+ err_t err = udp_bind(pcb, &pcb->local_ip, pcb->local_port);
+ if (err != ERR_OK) {
+ return err;
+ }
+ }
+
+ ip_addr_set_ipaddr(&pcb->remote_ip, ipaddr);
+ pcb->remote_port = port;
+ pcb->flags |= UDP_FLAGS_CONNECTED;
+
+ LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("udp_connect: connected to "));
+ ip_addr_debug_print(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+ &pcb->remote_ip);
+ LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, (", port %"U16_F")\n", pcb->remote_port));
+
+ /* Insert UDP PCB into the list of active UDP PCBs. */
+ for (ipcb = udp_pcbs; ipcb != NULL; ipcb = ipcb->next) {
+ if (pcb == ipcb) {
+ /* already on the list, just return */
+ return ERR_OK;
+ }
+ }
+ /* PCB not yet on the list, add PCB now */
+ pcb->next = udp_pcbs;
+ udp_pcbs = pcb;
+ return ERR_OK;
+}
+
+/**
+ * @ingroup udp_raw
+ * Disconnect a UDP PCB
+ *
+ * @param pcb the udp pcb to disconnect.
+ */
+void
+udp_disconnect(struct udp_pcb *pcb)
+{
+ /* reset remote address association */
+#if LWIP_IPV4 && LWIP_IPV6
+ if (IP_IS_ANY_TYPE_VAL(pcb->local_ip)) {
+ ip_addr_copy(pcb->remote_ip, *IP_ANY_TYPE);
+ } else {
+#endif
+ ip_addr_set_any(IP_IS_V6_VAL(pcb->remote_ip), &pcb->remote_ip);
+#if LWIP_IPV4 && LWIP_IPV6
+ }
+#endif
+ pcb->remote_port = 0;
+ /* mark PCB as unconnected */
+ pcb->flags &= ~UDP_FLAGS_CONNECTED;
+}
+
+/**
+ * @ingroup udp_raw
+ * Set a receive callback for a UDP PCB
+ *
+ * This callback will be called when receiving a datagram for the pcb.
+ *
+ * @param pcb the pcb for which to set the recv callback
+ * @param recv function pointer of the callback function
+ * @param recv_arg additional argument to pass to the callback function
+ */
+void
+udp_recv(struct udp_pcb *pcb, udp_recv_fn recv, void *recv_arg)
+{
+ /* remember recv() callback and user data */
+ pcb->recv = recv;
+ pcb->recv_arg = recv_arg;
+}
+
+/**
+ * @ingroup udp_raw
+ * Remove an UDP PCB.
+ *
+ * @param pcb UDP PCB to be removed. The PCB is removed from the list of
+ * UDP PCB's and the data structure is freed from memory.
+ *
+ * @see udp_new()
+ */
+void
+udp_remove(struct udp_pcb *pcb)
+{
+ struct udp_pcb *pcb2;
+
+ mib2_udp_unbind(pcb);
+ /* pcb to be removed is first in list? */
+ if (udp_pcbs == pcb) {
+ /* make list start at 2nd pcb */
+ udp_pcbs = udp_pcbs->next;
+ /* pcb not 1st in list */
+ } else {
+ for (pcb2 = udp_pcbs; pcb2 != NULL; pcb2 = pcb2->next) {
+ /* find pcb in udp_pcbs list */
+ if (pcb2->next != NULL && pcb2->next == pcb) {
+ /* remove pcb from list */
+ pcb2->next = pcb->next;
+ break;
+ }
+ }
+ }
+ memp_free(MEMP_UDP_PCB, pcb);
+}
+
+/**
+ * @ingroup udp_raw
+ * Create a UDP PCB.
+ *
+ * @return The UDP PCB which was created. NULL if the PCB data structure
+ * could not be allocated.
+ *
+ * @see udp_remove()
+ */
+struct udp_pcb *
+udp_new(void)
+{
+ struct udp_pcb *pcb;
+ pcb = (struct udp_pcb *)memp_malloc(MEMP_UDP_PCB);
+ /* could allocate UDP PCB? */
+ if (pcb != NULL) {
+ /* UDP Lite: by initializing to all zeroes, chksum_len is set to 0
+ * which means checksum is generated over the whole datagram per default
+ * (recommended as default by RFC 3828). */
+ /* initialize PCB to all zeroes */
+ memset(pcb, 0, sizeof(struct udp_pcb));
+ pcb->ttl = UDP_TTL;
+#if LWIP_MULTICAST_TX_OPTIONS
+ udp_set_multicast_ttl(pcb, UDP_TTL);
+#endif /* LWIP_MULTICAST_TX_OPTIONS */
+ }
+ return pcb;
+}
+
+/**
+ * @ingroup udp_raw
+ * Create a UDP PCB for specific IP type.
+ *
+ * @param type IP address type, see @ref lwip_ip_addr_type definitions.
+ * If you want to listen to IPv4 and IPv6 (dual-stack) packets,
+ * supply @ref IPADDR_TYPE_ANY as argument and bind to @ref IP_ANY_TYPE.
+ * @return The UDP PCB which was created. NULL if the PCB data structure
+ * could not be allocated.
+ *
+ * @see udp_remove()
+ */
+struct udp_pcb *
+udp_new_ip_type(u8_t type)
+{
+ struct udp_pcb *pcb;
+ pcb = udp_new();
+#if LWIP_IPV4 && LWIP_IPV6
+ if (pcb != NULL) {
+ IP_SET_TYPE_VAL(pcb->local_ip, type);
+ IP_SET_TYPE_VAL(pcb->remote_ip, type);
+ }
+#else
+ LWIP_UNUSED_ARG(type);
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+ return pcb;
+}
+
+/** This function is called from netif.c when address is changed
+ *
+ * @param old_addr IP address of the netif before change
+ * @param new_addr IP address of the netif after change
+ */
+void udp_netif_ip_addr_changed(const ip_addr_t* old_addr, const ip_addr_t* new_addr)
+{
+ struct udp_pcb* upcb;
+
+ if (!ip_addr_isany(old_addr) && !ip_addr_isany(new_addr)) {
+ for (upcb = udp_pcbs; upcb != NULL; upcb = upcb->next) {
+ /* PCB bound to current local interface address? */
+ if (ip_addr_cmp(&upcb->local_ip, old_addr)) {
+ /* The PCB is bound to the old ipaddr and
+ * is set to bound to the new one instead */
+ ip_addr_copy(upcb->local_ip, *new_addr);
+ }
+ }
+ }
+}
+
+#if UDP_DEBUG
+/**
+ * Print UDP header information for debug purposes.
+ *
+ * @param udphdr pointer to the udp header in memory.
+ */
+void
+udp_debug_print(struct udp_hdr *udphdr)
+{
+ LWIP_DEBUGF(UDP_DEBUG, ("UDP header:\n"));
+ LWIP_DEBUGF(UDP_DEBUG, ("+-------------------------------+\n"));
+ LWIP_DEBUGF(UDP_DEBUG, ("| %5"U16_F" | %5"U16_F" | (src port, dest port)\n",
+ lwip_ntohs(udphdr->src), lwip_ntohs(udphdr->dest)));
+ LWIP_DEBUGF(UDP_DEBUG, ("+-------------------------------+\n"));
+ LWIP_DEBUGF(UDP_DEBUG, ("| %5"U16_F" | 0x%04"X16_F" | (len, chksum)\n",
+ lwip_ntohs(udphdr->len), lwip_ntohs(udphdr->chksum)));
+ LWIP_DEBUGF(UDP_DEBUG, ("+-------------------------------+\n"));
+}
+#endif /* UDP_DEBUG */
+
+#endif /* LWIP_UDP */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/api.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/api.h
new file mode 100644
index 0000000..516bd16
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/api.h
@@ -0,0 +1,400 @@
+/**
+ * @file
+ * netconn API (to be used from non-TCPIP threads)
+ */
+
+/*
+ * 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_HDR_API_H
+#define LWIP_HDR_API_H
+
+#include "lwip/opt.h"
+
+#if LWIP_NETCONN || LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */
+/* Note: Netconn API is always available when sockets are enabled -
+ * sockets are implemented on top of them */
+
+#include "lwip/arch.h"
+#include "lwip/netbuf.h"
+#include "lwip/sys.h"
+#include "lwip/ip_addr.h"
+#include "lwip/err.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Throughout this file, IP addresses and port numbers are expected to be in
+ * the same byte order as in the corresponding pcb.
+ */
+
+/* Flags for netconn_write (u8_t) */
+#define NETCONN_NOFLAG 0x00
+#define NETCONN_NOCOPY 0x00 /* Only for source code compatibility */
+#define NETCONN_COPY 0x01
+#define NETCONN_MORE 0x02
+#define NETCONN_DONTBLOCK 0x04
+
+/* Flags for struct netconn.flags (u8_t) */
+/** Should this netconn avoid blocking? */
+#define NETCONN_FLAG_NON_BLOCKING 0x02
+/** Was the last connect action a non-blocking one? */
+#define NETCONN_FLAG_IN_NONBLOCKING_CONNECT 0x04
+/** If a nonblocking write has been rejected before, poll_tcp needs to
+ check if the netconn is writable again */
+#define NETCONN_FLAG_CHECK_WRITESPACE 0x10
+#if LWIP_IPV6
+/** If this flag is set then only IPv6 communication is allowed on the
+ netconn. As per RFC#3493 this features defaults to OFF allowing
+ dual-stack usage by default. */
+#define NETCONN_FLAG_IPV6_V6ONLY 0x20
+#endif /* LWIP_IPV6 */
+
+
+/* Helpers to process several netconn_types by the same code */
+#define NETCONNTYPE_GROUP(t) ((t)&0xF0)
+#define NETCONNTYPE_DATAGRAM(t) ((t)&0xE0)
+#if LWIP_IPV6
+#define NETCONN_TYPE_IPV6 0x08
+#define NETCONNTYPE_ISIPV6(t) (((t)&NETCONN_TYPE_IPV6) != 0)
+#define NETCONNTYPE_ISUDPLITE(t) (((t)&0xF3) == NETCONN_UDPLITE)
+#define NETCONNTYPE_ISUDPNOCHKSUM(t) (((t)&0xF3) == NETCONN_UDPNOCHKSUM)
+#else /* LWIP_IPV6 */
+#define NETCONNTYPE_ISIPV6(t) (0)
+#define NETCONNTYPE_ISUDPLITE(t) ((t) == NETCONN_UDPLITE)
+#define NETCONNTYPE_ISUDPNOCHKSUM(t) ((t) == NETCONN_UDPNOCHKSUM)
+#endif /* LWIP_IPV6 */
+
+/** @ingroup netconn_common
+ * Protocol family and type of the netconn
+ */
+enum netconn_type {
+ NETCONN_INVALID = 0,
+ /** TCP IPv4 */
+ NETCONN_TCP = 0x10,
+#if LWIP_IPV6
+ /** TCP IPv6 */
+ NETCONN_TCP_IPV6 = NETCONN_TCP | NETCONN_TYPE_IPV6 /* 0x18 */,
+#endif /* LWIP_IPV6 */
+ /** UDP IPv4 */
+ NETCONN_UDP = 0x20,
+ /** UDP IPv4 lite */
+ NETCONN_UDPLITE = 0x21,
+ /** UDP IPv4 no checksum */
+ NETCONN_UDPNOCHKSUM = 0x22,
+
+#if LWIP_IPV6
+ /** UDP IPv6 (dual-stack by default, unless you call @ref netconn_set_ipv6only) */
+ NETCONN_UDP_IPV6 = NETCONN_UDP | NETCONN_TYPE_IPV6 /* 0x28 */,
+ /** UDP IPv6 lite (dual-stack by default, unless you call @ref netconn_set_ipv6only) */
+ NETCONN_UDPLITE_IPV6 = NETCONN_UDPLITE | NETCONN_TYPE_IPV6 /* 0x29 */,
+ /** UDP IPv6 no checksum (dual-stack by default, unless you call @ref netconn_set_ipv6only) */
+ NETCONN_UDPNOCHKSUM_IPV6 = NETCONN_UDPNOCHKSUM | NETCONN_TYPE_IPV6 /* 0x2a */,
+#endif /* LWIP_IPV6 */
+
+ /** Raw connection IPv4 */
+ NETCONN_RAW = 0x40
+#if LWIP_IPV6
+ /** Raw connection IPv6 (dual-stack by default, unless you call @ref netconn_set_ipv6only) */
+ , NETCONN_RAW_IPV6 = NETCONN_RAW | NETCONN_TYPE_IPV6 /* 0x48 */
+#endif /* LWIP_IPV6 */
+};
+
+/** Current state of the netconn. Non-TCP netconns are always
+ * in state NETCONN_NONE! */
+enum netconn_state {
+ NETCONN_NONE,
+ NETCONN_WRITE,
+ NETCONN_LISTEN,
+ NETCONN_CONNECT,
+ NETCONN_CLOSE
+};
+
+/** Used to inform the callback function about changes
+ *
+ * Event explanation:
+ *
+ * In the netconn implementation, there are three ways to block a client:
+ *
+ * - accept mbox (sys_arch_mbox_fetch(&conn->acceptmbox, &accept_ptr, 0); in netconn_accept())
+ * - receive mbox (sys_arch_mbox_fetch(&conn->recvmbox, &buf, 0); in netconn_recv_data())
+ * - send queue is full (sys_arch_sem_wait(LWIP_API_MSG_SEM(msg), 0); in lwip_netconn_do_write())
+ *
+ * The events have to be seen as events signaling the state of these mboxes/semaphores. For non-blocking
+ * connections, you need to know in advance whether a call to a netconn function call would block or not,
+ * and these events tell you about that.
+ *
+ * RCVPLUS events say: Safe to perform a potentially blocking call call once more.
+ * They are counted in sockets - three RCVPLUS events for accept mbox means you are safe
+ * to call netconn_accept 3 times without being blocked.
+ * Same thing for receive mbox.
+ *
+ * RCVMINUS events say: Your call to to a possibly blocking function is "acknowledged".
+ * Socket implementation decrements the counter.
+ *
+ * For TX, there is no need to count, its merely a flag. SENDPLUS means you may send something.
+ * SENDPLUS occurs when enough data was delivered to peer so netconn_send() can be called again.
+ * A SENDMINUS event occurs when the next call to a netconn_send() would be blocking.
+ */
+enum netconn_evt {
+ NETCONN_EVT_RCVPLUS,
+ NETCONN_EVT_RCVMINUS,
+ NETCONN_EVT_SENDPLUS,
+ NETCONN_EVT_SENDMINUS,
+ NETCONN_EVT_ERROR
+};
+
+#if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD)
+/** Used for netconn_join_leave_group() */
+enum netconn_igmp {
+ NETCONN_JOIN,
+ NETCONN_LEAVE
+};
+#endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */
+
+#if LWIP_DNS
+/* Used for netconn_gethostbyname_addrtype(), these should match the DNS_ADDRTYPE defines in dns.h */
+#define NETCONN_DNS_DEFAULT NETCONN_DNS_IPV4_IPV6
+#define NETCONN_DNS_IPV4 0
+#define NETCONN_DNS_IPV6 1
+#define NETCONN_DNS_IPV4_IPV6 2 /* try to resolve IPv4 first, try IPv6 if IPv4 fails only */
+#define NETCONN_DNS_IPV6_IPV4 3 /* try to resolve IPv6 first, try IPv4 if IPv6 fails only */
+#endif /* LWIP_DNS */
+
+/* forward-declare some structs to avoid to include their headers */
+struct ip_pcb;
+struct tcp_pcb;
+struct udp_pcb;
+struct raw_pcb;
+struct netconn;
+struct api_msg;
+
+/** A callback prototype to inform about events for a netconn */
+typedef void (* netconn_callback)(struct netconn *, enum netconn_evt, u16_t len);
+
+/** A netconn descriptor */
+struct netconn {
+ /** type of the netconn (TCP, UDP or RAW) */
+ enum netconn_type type;
+ /** current state of the netconn */
+ enum netconn_state state;
+ /** the lwIP internal protocol control block */
+ union {
+ struct ip_pcb *ip;
+ struct tcp_pcb *tcp;
+ struct udp_pcb *udp;
+ struct raw_pcb *raw;
+ } pcb;
+ /** the last error this netconn had */
+ err_t last_err;
+#if !LWIP_NETCONN_SEM_PER_THREAD
+ /** sem that is used to synchronously execute functions in the core context */
+ sys_sem_t op_completed;
+#endif
+ /** mbox where received packets are stored until they are fetched
+ by the netconn application thread (can grow quite big) */
+ sys_mbox_t recvmbox;
+#if LWIP_TCP
+ /** mbox where new connections are stored until processed
+ by the application thread */
+ sys_mbox_t acceptmbox;
+#endif /* LWIP_TCP */
+ /** only used for socket layer */
+#if LWIP_SOCKET
+ int socket;
+#endif /* LWIP_SOCKET */
+#if LWIP_SO_SNDTIMEO
+ /** timeout to wait for sending data (which means enqueueing data for sending
+ in internal buffers) in milliseconds */
+ s32_t send_timeout;
+#endif /* LWIP_SO_RCVTIMEO */
+#if LWIP_SO_RCVTIMEO
+ /** timeout in milliseconds to wait for new data to be received
+ (or connections to arrive for listening netconns) */
+ int recv_timeout;
+#endif /* LWIP_SO_RCVTIMEO */
+#if LWIP_SO_RCVBUF
+ /** maximum amount of bytes queued in recvmbox
+ not used for TCP: adjust TCP_WND instead! */
+ int recv_bufsize;
+ /** number of bytes currently in recvmbox to be received,
+ tested against recv_bufsize to limit bytes on recvmbox
+ for UDP and RAW, used for FIONREAD */
+ int recv_avail;
+#endif /* LWIP_SO_RCVBUF */
+#if LWIP_SO_LINGER
+ /** values <0 mean linger is disabled, values > 0 are seconds to linger */
+ s16_t linger;
+#endif /* LWIP_SO_LINGER */
+ /** flags holding more netconn-internal state, see NETCONN_FLAG_* defines */
+ u8_t flags;
+#if LWIP_TCP
+ /** TCP: when data passed to netconn_write doesn't fit into the send buffer,
+ this temporarily stores how much is already sent. */
+ size_t write_offset;
+ /** TCP: when data passed to netconn_write doesn't fit into the send buffer,
+ this temporarily stores the message.
+ Also used during connect and close. */
+ struct api_msg *current_msg;
+#endif /* LWIP_TCP */
+ /** A callback function that is informed about events for this netconn */
+ netconn_callback callback;
+};
+
+/** Register an Network connection event */
+#define API_EVENT(c,e,l) if (c->callback) { \
+ (*c->callback)(c, e, l); \
+ }
+
+/** Set conn->last_err to err but don't overwrite fatal errors */
+#define NETCONN_SET_SAFE_ERR(conn, err) do { if ((conn) != NULL) { \
+ SYS_ARCH_DECL_PROTECT(netconn_set_safe_err_lev); \
+ SYS_ARCH_PROTECT(netconn_set_safe_err_lev); \
+ if (!ERR_IS_FATAL((conn)->last_err)) { \
+ (conn)->last_err = err; \
+ } \
+ SYS_ARCH_UNPROTECT(netconn_set_safe_err_lev); \
+}} while(0);
+
+/* Network connection functions: */
+
+/** @ingroup netconn_common
+ * Create new netconn connection
+ * @param t @ref netconn_type */
+#define netconn_new(t) netconn_new_with_proto_and_callback(t, 0, NULL)
+#define netconn_new_with_callback(t, c) netconn_new_with_proto_and_callback(t, 0, c)
+struct netconn *netconn_new_with_proto_and_callback(enum netconn_type t, u8_t proto,
+ netconn_callback callback);
+err_t netconn_delete(struct netconn *conn);
+/** Get the type of a netconn (as enum netconn_type). */
+#define netconn_type(conn) (conn->type)
+
+err_t netconn_getaddr(struct netconn *conn, ip_addr_t *addr,
+ u16_t *port, u8_t local);
+/** @ingroup netconn_common */
+#define netconn_peer(c,i,p) netconn_getaddr(c,i,p,0)
+/** @ingroup netconn_common */
+#define netconn_addr(c,i,p) netconn_getaddr(c,i,p,1)
+
+err_t netconn_bind(struct netconn *conn, const ip_addr_t *addr, u16_t port);
+err_t netconn_connect(struct netconn *conn, const ip_addr_t *addr, u16_t port);
+err_t netconn_disconnect (struct netconn *conn);
+err_t netconn_listen_with_backlog(struct netconn *conn, u8_t backlog);
+/** @ingroup netconn_tcp */
+#define netconn_listen(conn) netconn_listen_with_backlog(conn, TCP_DEFAULT_LISTEN_BACKLOG)
+err_t netconn_accept(struct netconn *conn, struct netconn **new_conn);
+err_t netconn_recv(struct netconn *conn, struct netbuf **new_buf);
+err_t netconn_recv_tcp_pbuf(struct netconn *conn, struct pbuf **new_buf);
+err_t netconn_sendto(struct netconn *conn, struct netbuf *buf,
+ const ip_addr_t *addr, u16_t port);
+err_t netconn_send(struct netconn *conn, struct netbuf *buf);
+err_t netconn_write_partly(struct netconn *conn, const void *dataptr, size_t size,
+ u8_t apiflags, size_t *bytes_written);
+/** @ingroup netconn_tcp */
+#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);
+
+#if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD)
+err_t netconn_join_leave_group(struct netconn *conn, const ip_addr_t *multiaddr,
+ const ip_addr_t *netif_addr, enum netconn_igmp join_or_leave);
+#endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */
+#if LWIP_DNS
+#if LWIP_IPV4 && LWIP_IPV6
+err_t netconn_gethostbyname_addrtype(const char *name, ip_addr_t *addr, u8_t dns_addrtype);
+#define netconn_gethostbyname(name, addr) netconn_gethostbyname_addrtype(name, addr, NETCONN_DNS_DEFAULT)
+#else /* LWIP_IPV4 && LWIP_IPV6 */
+err_t netconn_gethostbyname(const char *name, ip_addr_t *addr);
+#define netconn_gethostbyname_addrtype(name, addr, dns_addrtype) netconn_gethostbyname(name, addr)
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+#endif /* LWIP_DNS */
+
+#define netconn_err(conn) ((conn)->last_err)
+#define netconn_recv_bufsize(conn) ((conn)->recv_bufsize)
+
+/** Set the blocking status of netconn calls (@todo: write/send is missing) */
+#define netconn_set_nonblocking(conn, val) do { if(val) { \
+ (conn)->flags |= NETCONN_FLAG_NON_BLOCKING; \
+} else { \
+ (conn)->flags &= ~ NETCONN_FLAG_NON_BLOCKING; }} while(0)
+/** Get the blocking status of netconn calls (@todo: write/send is missing) */
+#define netconn_is_nonblocking(conn) (((conn)->flags & NETCONN_FLAG_NON_BLOCKING) != 0)
+
+#if LWIP_IPV6
+/** @ingroup netconn_common
+ * TCP: Set the IPv6 ONLY status of netconn calls (see NETCONN_FLAG_IPV6_V6ONLY)
+ */
+#define netconn_set_ipv6only(conn, val) do { if(val) { \
+ (conn)->flags |= NETCONN_FLAG_IPV6_V6ONLY; \
+} else { \
+ (conn)->flags &= ~ NETCONN_FLAG_IPV6_V6ONLY; }} while(0)
+/** @ingroup netconn_common
+ * TCP: Get the IPv6 ONLY status of netconn calls (see NETCONN_FLAG_IPV6_V6ONLY)
+ */
+#define netconn_get_ipv6only(conn) (((conn)->flags & NETCONN_FLAG_IPV6_V6ONLY) != 0)
+#endif /* LWIP_IPV6 */
+
+#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))
+/** Get the receive timeout in milliseconds */
+#define netconn_get_recvtimeout(conn) ((conn)->recv_timeout)
+#endif /* LWIP_SO_RCVTIMEO */
+#if LWIP_SO_RCVBUF
+/** Set the receive buffer in bytes */
+#define netconn_set_recvbufsize(conn, recvbufsize) ((conn)->recv_bufsize = (recvbufsize))
+/** Get the receive buffer in bytes */
+#define netconn_get_recvbufsize(conn) ((conn)->recv_bufsize)
+#endif /* LWIP_SO_RCVBUF*/
+
+#if LWIP_NETCONN_SEM_PER_THREAD
+void netconn_thread_init(void);
+void netconn_thread_cleanup(void);
+#else /* LWIP_NETCONN_SEM_PER_THREAD */
+#define netconn_thread_init()
+#define netconn_thread_cleanup()
+#endif /* LWIP_NETCONN_SEM_PER_THREAD */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_NETCONN || LWIP_SOCKET */
+
+#endif /* LWIP_HDR_API_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/api_msg.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/api_msg.h
new file mode 100644
index 0000000..7dcf01a
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/api_msg.h
@@ -0,0 +1,211 @@
+/*
+ * 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_HDR_API_MSG_H
+#define LWIP_HDR_API_MSG_H
+
+#include "lwip/opt.h"
+
+#if LWIP_NETCONN || LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */
+
+/* don't export the netconn functions when socket API is enabled but netconn API is disabled */
+#if LWIP_NETCONN
+#define LWIP_NETCONN_SCOPE
+#else /* LWIP_NETCONN */
+#define LWIP_NETCONN_SCOPE static
+#endif /* LWIP_NETCONN */
+
+#include <stddef.h> /* for size_t */
+
+#include "lwip/ip_addr.h"
+#include "lwip/err.h"
+#include "lwip/sys.h"
+#include "lwip/igmp.h"
+#include "lwip/api.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if LWIP_MPU_COMPATIBLE
+#define API_MSG_M_DEF(m) m
+#define API_MSG_M_DEF_C(t, m) t m
+#else /* LWIP_MPU_COMPATIBLE */
+#define API_MSG_M_DEF(m) *m
+#define API_MSG_M_DEF_C(t, m) const t * m
+#endif /* LWIP_MPU_COMPATIBLE */
+
+/* For the netconn API, these values are use as a bitmask! */
+#define NETCONN_SHUT_RD 1
+#define NETCONN_SHUT_WR 2
+#define NETCONN_SHUT_RDWR (NETCONN_SHUT_RD | NETCONN_SHUT_WR)
+
+/* IP addresses and port numbers are expected to be in
+ * the same byte order as in the corresponding pcb.
+ */
+/** This struct includes everything that is necessary to execute a function
+ for a netconn in another thread context (mainly used to process netconns
+ in the tcpip_thread context to be thread safe). */
+struct api_msg_msg {
+ /** The netconn which to process - always needed: it includes the semaphore
+ which is used to block the application thread until the function finished. */
+ struct netconn *conn;
+ /** The return value of the function executed in tcpip_thread. */
+ err_t err;
+ /** Depending on the executed function, one of these union members is used */
+ union {
+ /** used for lwip_netconn_do_send */
+ struct netbuf *b;
+ /** used for lwip_netconn_do_newconn */
+ struct {
+ u8_t proto;
+ } n;
+ /** used for lwip_netconn_do_bind and lwip_netconn_do_connect */
+ struct {
+ API_MSG_M_DEF_C(ip_addr_t, ipaddr);
+ u16_t port;
+ } bc;
+ /** used for lwip_netconn_do_getaddr */
+ struct {
+ ipX_addr_t API_MSG_M_DEF(ipaddr);
+ u16_t API_MSG_M_DEF(port);
+ u8_t local;
+ } ad;
+ /** used for lwip_netconn_do_write */
+ struct {
+ const void *dataptr;
+ size_t len;
+ u8_t apiflags;
+#if LWIP_SO_SNDTIMEO
+ u32_t time_started;
+#endif /* LWIP_SO_SNDTIMEO */
+ } w;
+ /** used for lwip_netconn_do_recv */
+ struct {
+ u32_t len;
+ } r;
+ /** used for lwip_netconn_do_close (/shutdown) */
+ struct {
+ u8_t shut;
+#if LWIP_SO_SNDTIMEO || LWIP_SO_LINGER
+ u32_t time_started;
+#else /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */
+ u8_t polls_left;
+#endif /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */
+ } sd;
+#if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD)
+ /** used for lwip_netconn_do_join_leave_group */
+ struct {
+ API_MSG_M_DEF_C(ipX_addr_t, multiaddr);
+ API_MSG_M_DEF_C(ipX_addr_t, netif_addr);
+ enum netconn_igmp join_or_leave;
+ } jl;
+#endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */
+#if TCP_LISTEN_BACKLOG
+ struct {
+ u8_t backlog;
+ } lb;
+#endif /* TCP_LISTEN_BACKLOG */
+ } msg;
+#if LWIP_NETCONN_SEM_PER_THREAD
+ sys_sem_t* op_completed_sem;
+#endif /* LWIP_NETCONN_SEM_PER_THREAD */
+};
+
+#if LWIP_NETCONN_SEM_PER_THREAD
+#define LWIP_API_MSG_SEM(msg) ((msg)->op_completed_sem)
+#else /* LWIP_NETCONN_SEM_PER_THREAD */
+#define LWIP_API_MSG_SEM(msg) (&(msg)->conn->op_completed)
+#endif /* LWIP_NETCONN_SEM_PER_THREAD */
+
+
+/** This struct contains a function to execute in another thread context and
+ a struct api_msg_msg that serves as an argument for this function.
+ This is passed to tcpip_apimsg to execute functions in tcpip_thread context. */
+struct api_msg {
+ /** function to execute in tcpip_thread context */
+ void (* function)(struct api_msg_msg *msg);
+ /** arguments for this function */
+ struct api_msg_msg msg;
+};
+
+#if LWIP_DNS
+/** As lwip_netconn_do_gethostbyname requires more arguments but doesn't require a netconn,
+ it has its own struct (to avoid struct api_msg getting bigger than necessary).
+ lwip_netconn_do_gethostbyname must be called using tcpip_callback instead of tcpip_apimsg
+ (see netconn_gethostbyname). */
+struct dns_api_msg {
+ /** Hostname to query or dotted IP address string */
+#if LWIP_MPU_COMPATIBLE
+ char name[DNS_MAX_NAME_LENGTH];
+#else /* LWIP_MPU_COMPATIBLE */
+ const char *name;
+#endif /* LWIP_MPU_COMPATIBLE */
+ /** The resolved address is stored here */
+ ip_addr_t API_MSG_M_DEF(addr);
+ /** This semaphore is posted when the name is resolved, the application thread
+ should wait on it. */
+ sys_sem_t API_MSG_M_DEF(sem);
+ /** Errors are given back here */
+ err_t API_MSG_M_DEF(err);
+};
+#endif /* LWIP_DNS */
+
+LWIP_NETCONN_SCOPE void lwip_netconn_do_newconn ( struct api_msg_msg *msg);
+LWIP_NETCONN_SCOPE void lwip_netconn_do_delconn ( struct api_msg_msg *msg);
+LWIP_NETCONN_SCOPE void lwip_netconn_do_bind ( struct api_msg_msg *msg);
+LWIP_NETCONN_SCOPE void lwip_netconn_do_connect ( struct api_msg_msg *msg);
+LWIP_NETCONN_SCOPE void lwip_netconn_do_disconnect ( struct api_msg_msg *msg);
+LWIP_NETCONN_SCOPE void lwip_netconn_do_listen ( struct api_msg_msg *msg);
+LWIP_NETCONN_SCOPE void lwip_netconn_do_send ( struct api_msg_msg *msg);
+LWIP_NETCONN_SCOPE void lwip_netconn_do_recv ( struct api_msg_msg *msg);
+LWIP_NETCONN_SCOPE void lwip_netconn_do_write ( struct api_msg_msg *msg);
+LWIP_NETCONN_SCOPE void lwip_netconn_do_getaddr ( struct api_msg_msg *msg);
+LWIP_NETCONN_SCOPE void lwip_netconn_do_close ( struct api_msg_msg *msg);
+LWIP_NETCONN_SCOPE void lwip_netconn_do_shutdown ( struct api_msg_msg *msg);
+#if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD)
+LWIP_NETCONN_SCOPE void lwip_netconn_do_join_leave_group( struct api_msg_msg *msg);
+#endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */
+
+#if LWIP_DNS
+LWIP_NETCONN_SCOPE void lwip_netconn_do_gethostbyname(void *arg);
+#endif /* LWIP_DNS */
+
+LWIP_NETCONN_SCOPE struct netconn* netconn_alloc(enum netconn_type t, netconn_callback callback);
+LWIP_NETCONN_SCOPE void netconn_free(struct netconn *conn);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_NETCONN || LWIP_SOCKET */
+
+#endif /* LWIP_HDR_API_MSG_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/FILES b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/FILES
new file mode 100644
index 0000000..adfc0f3
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/FILES
@@ -0,0 +1,2 @@
+This directory contains application headers.
+Every application shall provide one api file APP.h and optionally one options file APP_opts.h
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/fs.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/fs.h
new file mode 100644
index 0000000..bb176fa
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/fs.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef LWIP_HDR_APPS_FS_H
+#define LWIP_HDR_APPS_FS_H
+
+#include "httpd_opts.h"
+#include "lwip/err.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define FS_READ_EOF -1
+#define FS_READ_DELAYED -2
+
+#if HTTPD_PRECALCULATED_CHECKSUM
+struct fsdata_chksum {
+ u32_t offset;
+ u16_t chksum;
+ u16_t len;
+};
+#endif /* HTTPD_PRECALCULATED_CHECKSUM */
+
+#define FS_FILE_FLAGS_HEADER_INCLUDED 0x01
+#define FS_FILE_FLAGS_HEADER_PERSISTENT 0x02
+
+struct fs_file {
+ const char *data;
+ int len;
+ int index;
+ void *pextension;
+#if HTTPD_PRECALCULATED_CHECKSUM
+ const struct fsdata_chksum *chksum;
+ u16_t chksum_count;
+#endif /* HTTPD_PRECALCULATED_CHECKSUM */
+ u8_t flags;
+#if LWIP_HTTPD_CUSTOM_FILES
+ u8_t is_custom_file;
+#endif /* LWIP_HTTPD_CUSTOM_FILES */
+#if LWIP_HTTPD_FILE_STATE
+ void *state;
+#endif /* LWIP_HTTPD_FILE_STATE */
+};
+
+#if LWIP_HTTPD_FS_ASYNC_READ
+typedef void (*fs_wait_cb)(void *arg);
+#endif /* LWIP_HTTPD_FS_ASYNC_READ */
+
+err_t fs_open(struct fs_file *file, const char *name);
+void fs_close(struct fs_file *file);
+#if LWIP_HTTPD_DYNAMIC_FILE_READ
+#if LWIP_HTTPD_FS_ASYNC_READ
+int fs_read_async(struct fs_file *file, char *buffer, int count, fs_wait_cb callback_fn, void *callback_arg);
+#else /* LWIP_HTTPD_FS_ASYNC_READ */
+int fs_read(struct fs_file *file, char *buffer, int count);
+#endif /* LWIP_HTTPD_FS_ASYNC_READ */
+#endif /* LWIP_HTTPD_DYNAMIC_FILE_READ */
+#if LWIP_HTTPD_FS_ASYNC_READ
+int fs_is_file_ready(struct fs_file *file, fs_wait_cb callback_fn, void *callback_arg);
+#endif /* LWIP_HTTPD_FS_ASYNC_READ */
+int fs_bytes_left(struct fs_file *file);
+
+#if LWIP_HTTPD_FILE_STATE
+/** This user-defined function is called when a file is opened. */
+void *fs_state_init(struct fs_file *file, const char *name);
+/** This user-defined function is called when a file is closed. */
+void fs_state_free(struct fs_file *file, void *state);
+#endif /* #if LWIP_HTTPD_FILE_STATE */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_APPS_FS_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/httpd.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/httpd.h
new file mode 100644
index 0000000..40f1811
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/httpd.h
@@ -0,0 +1,236 @@
+/**
+ * @file
+ * HTTP server
+ */
+
+/*
+ * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ * This version of the file has been modified by Texas Instruments to offer
+ * simple server-side-include (SSI) and Common Gateway Interface (CGI)
+ * capability.
+ */
+
+#ifndef LWIP_HDR_APPS_HTTPD_H
+#define LWIP_HDR_APPS_HTTPD_H
+
+#include "httpd_opts.h"
+#include "lwip/err.h"
+#include "lwip/pbuf.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if LWIP_HTTPD_CGI
+
+/*
+ * Function pointer for a CGI script handler.
+ *
+ * This function is called each time the HTTPD server is asked for a file
+ * whose name was previously registered as a CGI function using a call to
+ * http_set_cgi_handler. The iIndex parameter provides the index of the
+ * CGI within the ppcURLs array passed to http_set_cgi_handler. Parameters
+ * pcParam and pcValue provide access to the parameters provided along with
+ * the URI. iNumParams provides a count of the entries in the pcParam and
+ * pcValue arrays. Each entry in the pcParam array contains the name of a
+ * parameter with the corresponding entry in the pcValue array containing the
+ * value for that parameter. Note that pcParam may contain multiple elements
+ * with the same name if, for example, a multi-selection list control is used
+ * in the form generating the data.
+ *
+ * The function should return a pointer to a character string which is the
+ * path and filename of the response that is to be sent to the connected
+ * browser, for example "/thanks.htm" or "/response/error.ssi".
+ *
+ * The maximum number of parameters that will be passed to this function via
+ * iNumParams is defined by LWIP_HTTPD_MAX_CGI_PARAMETERS. Any parameters in the incoming
+ * HTTP request above this number will be discarded.
+ *
+ * Requests intended for use by this CGI mechanism must be sent using the GET
+ * method (which encodes all parameters within the URI rather than in a block
+ * later in the request). Attempts to use the POST method will result in the
+ * request being ignored.
+ *
+ */
+typedef const char *(*tCGIHandler)(int iIndex, int iNumParams, char *pcParam[],
+ char *pcValue[]);
+
+/*
+ * Structure defining the base filename (URL) of a CGI and the associated
+ * function which is to be called when that URL is requested.
+ */
+typedef struct
+{
+ const char *pcCGIName;
+ tCGIHandler pfnCGIHandler;
+} tCGI;
+
+void http_set_cgi_handlers(const tCGI *pCGIs, int iNumHandlers);
+
+#endif /* LWIP_HTTPD_CGI */
+
+#if LWIP_HTTPD_CGI || LWIP_HTTPD_CGI_SSI
+
+#if LWIP_HTTPD_CGI_SSI
+/** Define this generic CGI handler in your application.
+ * It is called once for every URI with parameters.
+ * The parameters can be stored to
+ */
+extern void httpd_cgi_handler(const char* uri, int iNumParams, char **pcParam, char **pcValue
+#if defined(LWIP_HTTPD_FILE_STATE) && LWIP_HTTPD_FILE_STATE
+ , void *connection_state
+#endif /* LWIP_HTTPD_FILE_STATE */
+ );
+#endif /* LWIP_HTTPD_CGI_SSI */
+
+#endif /* LWIP_HTTPD_CGI || LWIP_HTTPD_CGI_SSI */
+
+#if LWIP_HTTPD_SSI
+
+/*
+ * Function pointer for the SSI tag handler callback.
+ *
+ * This function will be called each time the HTTPD server detects a tag of the
+ * form <!--#name--> in a .shtml, .ssi or .shtm file where "name" appears as
+ * one of the tags supplied to http_set_ssi_handler in the ppcTags array. The
+ * returned insert string, which will be appended after the the string
+ * "<!--#name-->" in file sent back to the client,should be written to pointer
+ * pcInsert. iInsertLen contains the size of the buffer pointed to by
+ * pcInsert. The iIndex parameter provides the zero-based index of the tag as
+ * found in the ppcTags array and identifies the tag that is to be processed.
+ *
+ * The handler returns the number of characters written to pcInsert excluding
+ * any terminating NULL or a negative number to indicate a failure (tag not
+ * recognized, for example).
+ *
+ * Note that the behavior of this SSI mechanism is somewhat different from the
+ * "normal" SSI processing as found in, for example, the Apache web server. In
+ * this case, the inserted text is appended following the SSI tag rather than
+ * replacing the tag entirely. This allows for an implementation that does not
+ * require significant additional buffering of output data yet which will still
+ * offer usable SSI functionality. One downside to this approach is when
+ * attempting to use SSI within JavaScript. The SSI tag is structured to
+ * resemble an HTML comment but this syntax does not constitute a comment
+ * within JavaScript and, hence, leaving the tag in place will result in
+ * problems in these cases. To work around this, any SSI tag which needs to
+ * output JavaScript code must do so in an encapsulated way, sending the whole
+ * HTML <script>...</script> section as a single include.
+ */
+typedef u16_t (*tSSIHandler)(
+#if LWIP_HTTPD_SSI_RAW
+ const char* ssi_tag_name,
+#else /* LWIP_HTTPD_SSI_RAW */
+ int iIndex,
+#endif /* LWIP_HTTPD_SSI_RAW */
+ char *pcInsert, int iInsertLen
+#if LWIP_HTTPD_SSI_MULTIPART
+ , u16_t current_tag_part, u16_t *next_tag_part
+#endif /* LWIP_HTTPD_SSI_MULTIPART */
+#if defined(LWIP_HTTPD_FILE_STATE) && LWIP_HTTPD_FILE_STATE
+ , void *connection_state
+#endif /* LWIP_HTTPD_FILE_STATE */
+ );
+
+/** Set the SSI handler function
+ * (if LWIP_HTTPD_SSI_RAW==1, only the first argument is used)
+ */
+void http_set_ssi_handler(tSSIHandler pfnSSIHandler,
+ const char **ppcTags, int iNumTags);
+
+/** For LWIP_HTTPD_SSI_RAW==1, return this to indicate the tag is unknown.
+ * In this case, the webserver writes a warning into the page.
+ * You can also just return 0 to write nothing for unknown tags.
+ */
+#define HTTPD_SSI_TAG_UNKNOWN 0xFFFF
+
+#endif /* LWIP_HTTPD_SSI */
+
+#if LWIP_HTTPD_SUPPORT_POST
+
+/* These functions must be implemented by the application */
+
+/** Called when a POST request has been received. The application can decide
+ * whether to accept it or not.
+ *
+ * @param connection Unique connection identifier, valid until httpd_post_end
+ * is called.
+ * @param uri The HTTP header URI receiving the POST request.
+ * @param http_request The raw HTTP request (the first packet, normally).
+ * @param http_request_len Size of 'http_request'.
+ * @param content_len Content-Length from HTTP header.
+ * @param response_uri Filename of response file, to be filled when denying the
+ * request
+ * @param response_uri_len Size of the 'response_uri' buffer.
+ * @param post_auto_wnd Set this to 0 to let the callback code handle window
+ * updates by calling 'httpd_post_data_recved' (to throttle rx speed)
+ * default is 1 (httpd handles window updates automatically)
+ * @return ERR_OK: Accept the POST request, data may be passed in
+ * another err_t: Deny the POST request, send back 'bad request'.
+ */
+err_t httpd_post_begin(void *connection, const char *uri, const char *http_request,
+ u16_t http_request_len, int content_len, char *response_uri,
+ u16_t response_uri_len, u8_t *post_auto_wnd);
+
+/** Called for each pbuf of data that has been received for a POST.
+ * ATTENTION: The application is responsible for freeing the pbufs passed in!
+ *
+ * @param connection Unique connection identifier.
+ * @param p Received data.
+ * @return ERR_OK: Data accepted.
+ * another err_t: Data denied, http_post_get_response_uri will be called.
+ */
+err_t httpd_post_receive_data(void *connection, struct pbuf *p);
+
+/** Called when all data is received or when the connection is closed.
+ * The application must return the filename/URI of a file to send in response
+ * to this POST request. If the response_uri buffer is untouched, a 404
+ * response is returned.
+ *
+ * @param connection Unique connection identifier.
+ * @param response_uri Filename of response file, to be filled when denying the request
+ * @param response_uri_len Size of the 'response_uri' buffer.
+ */
+void httpd_post_finished(void *connection, char *response_uri, u16_t response_uri_len);
+
+#if LWIP_HTTPD_POST_MANUAL_WND
+void httpd_post_data_recved(void *connection, u16_t recved_len);
+#endif /* LWIP_HTTPD_POST_MANUAL_WND */
+
+#endif /* LWIP_HTTPD_SUPPORT_POST */
+
+void httpd_init(void);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HTTPD_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/httpd_opts.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/httpd_opts.h
new file mode 100644
index 0000000..340db15
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/httpd_opts.h
@@ -0,0 +1,323 @@
+/**
+ * @file
+ * HTTP server options list
+ */
+
+/*
+ * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ * This version of the file has been modified by Texas Instruments to offer
+ * simple server-side-include (SSI) and Common Gateway Interface (CGI)
+ * capability.
+ */
+
+#ifndef LWIP_HDR_APPS_HTTPD_OPTS_H
+#define LWIP_HDR_APPS_HTTPD_OPTS_H
+
+#include "lwip/opt.h"
+
+/**
+ * @defgroup httpd_opts Options
+ * @ingroup httpd
+ * @{
+ */
+
+/** Set this to 1 to support CGI (old style) */
+#if !defined LWIP_HTTPD_CGI || defined __DOXYGEN__
+#define LWIP_HTTPD_CGI 0
+#endif
+
+/** Set this to 1 to support CGI (new style) */
+#if !defined LWIP_HTTPD_CGI_SSI || defined __DOXYGEN__
+#define LWIP_HTTPD_CGI_SSI 0
+#endif
+
+/** Set this to 1 to support SSI (Server-Side-Includes) */
+#if !defined LWIP_HTTPD_SSI || defined __DOXYGEN__
+#define LWIP_HTTPD_SSI 0
+#endif
+
+/** Set this to 1 to implement an SSI tag handler callback that gets a const char*
+ * to the tag (instead of an index into a pre-registered array of known tags) */
+#if !defined LWIP_HTTPD_SSI_RAW || defined __DOXYGEN__
+#define LWIP_HTTPD_SSI_RAW 0
+#endif
+
+/** Set this to 1 to support HTTP POST */
+#if !defined LWIP_HTTPD_SUPPORT_POST || defined __DOXYGEN__
+#define LWIP_HTTPD_SUPPORT_POST 0
+#endif
+
+/* The maximum number of parameters that the CGI handler can be sent. */
+#if !defined LWIP_HTTPD_MAX_CGI_PARAMETERS || defined __DOXYGEN__
+#define LWIP_HTTPD_MAX_CGI_PARAMETERS 16
+#endif
+
+/** LWIP_HTTPD_SSI_MULTIPART==1: SSI handler function is called with 2 more
+ * arguments indicating a counter for insert string that are too long to be
+ * inserted at once: the SSI handler function must then set 'next_tag_part'
+ * which will be passed back to it in the next call. */
+#if !defined LWIP_HTTPD_SSI_MULTIPART || defined __DOXYGEN__
+#define LWIP_HTTPD_SSI_MULTIPART 0
+#endif
+
+/* The maximum length of the string comprising the tag name */
+#if !defined LWIP_HTTPD_MAX_TAG_NAME_LEN || defined __DOXYGEN__
+#define LWIP_HTTPD_MAX_TAG_NAME_LEN 8
+#endif
+
+/* The maximum length of string that can be returned to replace any given tag */
+#if !defined LWIP_HTTPD_MAX_TAG_INSERT_LEN || defined __DOXYGEN__
+#define LWIP_HTTPD_MAX_TAG_INSERT_LEN 192
+#endif
+
+#if !defined LWIP_HTTPD_POST_MANUAL_WND || defined __DOXYGEN__
+#define LWIP_HTTPD_POST_MANUAL_WND 0
+#endif
+
+/** This string is passed in the HTTP header as "Server: " */
+#if !defined HTTPD_SERVER_AGENT || defined __DOXYGEN__
+#define HTTPD_SERVER_AGENT "lwIP/" LWIP_VERSION_STRING " (http://savannah.nongnu.org/projects/lwip)"
+#endif
+
+/** Set this to 1 if you want to include code that creates HTTP headers
+ * at runtime. Default is off: HTTP headers are then created statically
+ * by the makefsdata tool. Static headers mean smaller code size, but
+ * the (readonly) fsdata will grow a bit as every file includes the HTTP
+ * header. */
+#if !defined LWIP_HTTPD_DYNAMIC_HEADERS || defined __DOXYGEN__
+#define LWIP_HTTPD_DYNAMIC_HEADERS 0
+#endif
+
+#if !defined HTTPD_DEBUG || defined __DOXYGEN__
+#define HTTPD_DEBUG LWIP_DBG_OFF
+#endif
+
+/** Set this to 1 to use a memp pool for allocating
+ * struct http_state instead of the heap.
+ */
+#if !defined HTTPD_USE_MEM_POOL || defined __DOXYGEN__
+#define HTTPD_USE_MEM_POOL 0
+#endif
+
+/** The server port for HTTPD to use */
+#if !defined HTTPD_SERVER_PORT || defined __DOXYGEN__
+#define HTTPD_SERVER_PORT 80
+#endif
+
+/** Maximum retries before the connection is aborted/closed.
+ * - number of times pcb->poll is called -> default is 4*500ms = 2s;
+ * - reset when pcb->sent is called
+ */
+#if !defined HTTPD_MAX_RETRIES || defined __DOXYGEN__
+#define HTTPD_MAX_RETRIES 4
+#endif
+
+/** The poll delay is X*500ms */
+#if !defined HTTPD_POLL_INTERVAL || defined __DOXYGEN__
+#define HTTPD_POLL_INTERVAL 4
+#endif
+
+/** Priority for tcp pcbs created by HTTPD (very low by default).
+ * Lower priorities get killed first when running out of memory.
+ */
+#if !defined HTTPD_TCP_PRIO || defined __DOXYGEN__
+#define HTTPD_TCP_PRIO TCP_PRIO_MIN
+#endif
+
+/** Set this to 1 to enable timing each file sent */
+#if !defined LWIP_HTTPD_TIMING || defined __DOXYGEN__
+#define LWIP_HTTPD_TIMING 0
+#endif
+/** Set this to 1 to enable timing each file sent */
+#if !defined HTTPD_DEBUG_TIMING || defined __DOXYGEN__
+#define HTTPD_DEBUG_TIMING LWIP_DBG_OFF
+#endif
+
+/** Set this to one to show error pages when parsing a request fails instead
+ of simply closing the connection. */
+#if !defined LWIP_HTTPD_SUPPORT_EXTSTATUS || defined __DOXYGEN__
+#define LWIP_HTTPD_SUPPORT_EXTSTATUS 0
+#endif
+
+/** Set this to 0 to drop support for HTTP/0.9 clients (to save some bytes) */
+#if !defined LWIP_HTTPD_SUPPORT_V09 || defined __DOXYGEN__
+#define LWIP_HTTPD_SUPPORT_V09 1
+#endif
+
+/** Set this to 1 to enable HTTP/1.1 persistent connections.
+ * ATTENTION: If the generated file system includes HTTP headers, these must
+ * include the "Connection: keep-alive" header (pass argument "-11" to makefsdata).
+ */
+#if !defined LWIP_HTTPD_SUPPORT_11_KEEPALIVE || defined __DOXYGEN__
+#define LWIP_HTTPD_SUPPORT_11_KEEPALIVE 0
+#endif
+
+/** Set this to 1 to support HTTP request coming in in multiple packets/pbufs */
+#if !defined LWIP_HTTPD_SUPPORT_REQUESTLIST || defined __DOXYGEN__
+#define LWIP_HTTPD_SUPPORT_REQUESTLIST 1
+#endif
+
+#if LWIP_HTTPD_SUPPORT_REQUESTLIST
+/** Number of rx pbufs to enqueue to parse an incoming request (up to the first
+ newline) */
+#if !defined LWIP_HTTPD_REQ_QUEUELEN || defined __DOXYGEN__
+#define LWIP_HTTPD_REQ_QUEUELEN 5
+#endif
+
+/** Number of (TCP payload-) bytes (in pbufs) to enqueue to parse and incoming
+ request (up to the first double-newline) */
+#if !defined LWIP_HTTPD_REQ_BUFSIZE || defined __DOXYGEN__
+#define LWIP_HTTPD_REQ_BUFSIZE LWIP_HTTPD_MAX_REQ_LENGTH
+#endif
+
+/** Defines the maximum length of a HTTP request line (up to the first CRLF,
+ copied from pbuf into this a global buffer when pbuf- or packet-queues
+ are received - otherwise the input pbuf is used directly) */
+#if !defined LWIP_HTTPD_MAX_REQ_LENGTH || defined __DOXYGEN__
+#define LWIP_HTTPD_MAX_REQ_LENGTH LWIP_MIN(1023, (LWIP_HTTPD_REQ_QUEUELEN * PBUF_POOL_BUFSIZE))
+#endif
+#endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */
+
+/** This is the size of a static buffer used when URIs end with '/'.
+ * In this buffer, the directory requested is concatenated with all the
+ * configured default file names.
+ * Set to 0 to disable checking default filenames on non-root directories.
+ */
+#if !defined LWIP_HTTPD_MAX_REQUEST_URI_LEN || defined __DOXYGEN__
+#define LWIP_HTTPD_MAX_REQUEST_URI_LEN 63
+#endif
+
+/** Maximum length of the filename to send as response to a POST request,
+ * filled in by the application when a POST is finished.
+ */
+#if !defined LWIP_HTTPD_POST_MAX_RESPONSE_URI_LEN || defined __DOXYGEN__
+#define LWIP_HTTPD_POST_MAX_RESPONSE_URI_LEN 63
+#endif
+
+/** Set this to 0 to not send the SSI tag (default is on, so the tag will
+ * be sent in the HTML page */
+#if !defined LWIP_HTTPD_SSI_INCLUDE_TAG || defined __DOXYGEN__
+#define LWIP_HTTPD_SSI_INCLUDE_TAG 1
+#endif
+
+/** Set this to 1 to call tcp_abort when tcp_close fails with memory error.
+ * This can be used to prevent consuming all memory in situations where the
+ * HTTP server has low priority compared to other communication. */
+#if !defined LWIP_HTTPD_ABORT_ON_CLOSE_MEM_ERROR || defined __DOXYGEN__
+#define LWIP_HTTPD_ABORT_ON_CLOSE_MEM_ERROR 0
+#endif
+
+/** Set this to 1 to kill the oldest connection when running out of
+ * memory for 'struct http_state' or 'struct http_ssi_state'.
+ * ATTENTION: This puts all connections on a linked list, so may be kind of slow.
+ */
+#if !defined LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED || defined __DOXYGEN__
+#define LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED 0
+#endif
+
+/** Set this to 1 to send URIs without extension without headers
+ * (who uses this at all??) */
+#if !defined LWIP_HTTPD_OMIT_HEADER_FOR_EXTENSIONLESS_URI || defined __DOXYGEN__
+#define LWIP_HTTPD_OMIT_HEADER_FOR_EXTENSIONLESS_URI 0
+#endif
+
+/** Default: Tags are sent from struct http_state and are therefore volatile */
+#if !defined HTTP_IS_TAG_VOLATILE || defined __DOXYGEN__
+#define HTTP_IS_TAG_VOLATILE(ptr) TCP_WRITE_FLAG_COPY
+#endif
+
+/* By default, the httpd is limited to send 2*pcb->mss to keep resource usage low
+ when http is not an important protocol in the device. */
+#if !defined HTTPD_LIMIT_SENDING_TO_2MSS || defined __DOXYGEN__
+#define HTTPD_LIMIT_SENDING_TO_2MSS 1
+#endif
+
+/* Define this to a function that returns the maximum amount of data to enqueue.
+ The function have this signature: u16_t fn(struct tcp_pcb* pcb); */
+#if !defined HTTPD_MAX_WRITE_LEN || defined __DOXYGEN__
+#if HTTPD_LIMIT_SENDING_TO_2MSS
+#define HTTPD_MAX_WRITE_LEN(pcb) (2 * tcp_mss(pcb))
+#endif
+#endif
+
+/*------------------- FS OPTIONS -------------------*/
+
+/** Set this to 1 and provide the functions:
+ * - "int fs_open_custom(struct fs_file *file, const char *name)"
+ * Called first for every opened file to allow opening files
+ * that are not included in fsdata(_custom).c
+ * - "void fs_close_custom(struct fs_file *file)"
+ * Called to free resources allocated by fs_open_custom().
+ */
+#if !defined LWIP_HTTPD_CUSTOM_FILES || defined __DOXYGEN__
+#define LWIP_HTTPD_CUSTOM_FILES 0
+#endif
+
+/** Set this to 1 to support fs_read() to dynamically read file data.
+ * Without this (default=off), only one-block files are supported,
+ * and the contents must be ready after fs_open().
+ */
+#if !defined LWIP_HTTPD_DYNAMIC_FILE_READ || defined __DOXYGEN__
+#define LWIP_HTTPD_DYNAMIC_FILE_READ 0
+#endif
+
+/** Set this to 1 to include an application state argument per file
+ * that is opened. This allows to keep a state per connection/file.
+ */
+#if !defined LWIP_HTTPD_FILE_STATE || defined __DOXYGEN__
+#define LWIP_HTTPD_FILE_STATE 0
+#endif
+
+/** HTTPD_PRECALCULATED_CHECKSUM==1: include precompiled checksums for
+ * predefined (MSS-sized) chunks of the files to prevent having to calculate
+ * the checksums at runtime. */
+#if !defined HTTPD_PRECALCULATED_CHECKSUM || defined __DOXYGEN__
+#define HTTPD_PRECALCULATED_CHECKSUM 0
+#endif
+
+/** LWIP_HTTPD_FS_ASYNC_READ==1: support asynchronous read operations
+ * (fs_read_async returns FS_READ_DELAYED and calls a callback when finished).
+ */
+#if !defined LWIP_HTTPD_FS_ASYNC_READ || defined __DOXYGEN__
+#define LWIP_HTTPD_FS_ASYNC_READ 0
+#endif
+
+/** Set this to 1 to include "fsdata_custom.c" instead of "fsdata.c" for the
+ * file system (to prevent changing the file included in CVS) */
+#if !defined HTTPD_USE_CUSTOM_FSDATA || defined __DOXYGEN__
+#define HTTPD_USE_CUSTOM_FSDATA 0
+#endif
+
+/**
+ * @}
+ */
+
+#endif /* LWIP_HDR_APPS_HTTPD_OPTS_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/lwiperf.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/lwiperf.h
new file mode 100644
index 0000000..7dbebb0
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/lwiperf.h
@@ -0,0 +1,84 @@
+/**
+ * @file
+ * lwIP iPerf server implementation
+ */
+
+/*
+ * Copyright (c) 2014 Simon Goldschmidt
+ * 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 LWIP_HDR_APPS_LWIPERF_H
+#define LWIP_HDR_APPS_LWIPERF_H
+
+#include "lwip/opt.h"
+#include "lwip/ip_addr.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define LWIPERF_TCP_PORT_DEFAULT 5001
+
+/** lwIPerf test results */
+enum lwiperf_report_type
+{
+ /** The server side test is done */
+ LWIPERF_TCP_DONE_SERVER,
+ /** The client side test is done */
+ LWIPERF_TCP_DONE_CLIENT,
+ /** Local error lead to test abort */
+ LWIPERF_TCP_ABORTED_LOCAL,
+ /** Data check error lead to test abort */
+ LWIPERF_TCP_ABORTED_LOCAL_DATAERROR,
+ /** Transmit error lead to test abort */
+ LWIPERF_TCP_ABORTED_LOCAL_TXERROR,
+ /** Remote side aborted the test */
+ LWIPERF_TCP_ABORTED_REMOTE
+};
+
+/** Prototype of a report function that is called when a session is finished.
+ This report function can show the test results.
+ @param report_type contains the test result */
+typedef void (*lwiperf_report_fn)(void *arg, enum lwiperf_report_type report_type,
+ const ip_addr_t* local_addr, u16_t local_port, const ip_addr_t* remote_addr, u16_t remote_port,
+ u32_t bytes_transferred, u32_t ms_duration, u32_t bandwidth_kbitpsec);
+
+
+void* lwiperf_start_tcp_server(const ip_addr_t* local_addr, u16_t local_port,
+ lwiperf_report_fn report_fn, void* report_arg);
+void* lwiperf_start_tcp_server_default(lwiperf_report_fn report_fn, void* report_arg);
+void lwiperf_abort(void* lwiperf_session);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_APPS_LWIPERF_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/mdns.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/mdns.h
new file mode 100644
index 0000000..d036816
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/mdns.h
@@ -0,0 +1,69 @@
+/**
+ * @file
+ * MDNS responder
+ */
+
+ /*
+ * Copyright (c) 2015 Verisure Innovation AB
+ * 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: Erik Ekman <erik@kryo.se>
+ *
+ */
+#ifndef LWIP_HDR_MDNS_H
+#define LWIP_HDR_MDNS_H
+
+#include "lwip/apps/mdns_opts.h"
+#include "lwip/netif.h"
+
+#if LWIP_MDNS_RESPONDER
+
+enum mdns_sd_proto {
+ DNSSD_PROTO_UDP = 0,
+ DNSSD_PROTO_TCP = 1
+};
+
+#define MDNS_LABEL_MAXLEN 63
+
+struct mdns_host;
+struct mdns_service;
+
+/** Callback function to add text to a reply, called when generating the reply */
+typedef void (*service_get_txt_fn_t)(struct mdns_service *service, void *txt_userdata);
+
+void mdns_resp_init(void);
+
+err_t mdns_resp_add_netif(struct netif *netif, const char *hostname, u32_t dns_ttl);
+err_t mdns_resp_remove_netif(struct netif *netif);
+
+err_t mdns_resp_add_service(struct netif *netif, const char *name, const char *service, enum mdns_sd_proto proto, u16_t port, u32_t dns_ttl, service_get_txt_fn_t txt_fn, void *txt_userdata);
+err_t mdns_resp_add_service_txtitem(struct mdns_service *service, const char *txt, u8_t txt_len);
+void mdns_resp_netif_settings_changed(struct netif *netif);
+
+#endif /* LWIP_MDNS_RESPONDER */
+
+#endif /* LWIP_HDR_MDNS_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/mdns_opts.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/mdns_opts.h
new file mode 100644
index 0000000..bf186bc
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/mdns_opts.h
@@ -0,0 +1,74 @@
+/**
+ * @file
+ * MDNS responder
+ */
+
+ /*
+ * Copyright (c) 2015 Verisure Innovation AB
+ * 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: Erik Ekman <erik@kryo.se>
+ *
+ */
+
+#ifndef LWIP_HDR_APPS_MDNS_OPTS_H
+#define LWIP_HDR_APPS_MDNS_OPTS_H
+
+#include "lwip/opt.h"
+
+/**
+ * @defgroup mdns_opts Options
+ * @ingroup mdns
+ * @{
+ */
+
+/**
+ * LWIP_MDNS_RESPONDER==1: Turn on multicast DNS module. UDP must be available for MDNS
+ * transport. IGMP is needed for IPv4 multicast.
+ */
+#ifndef LWIP_MDNS_RESPONDER
+#define LWIP_MDNS_RESPONDER 0
+#endif /* LWIP_MDNS_RESPONDER */
+
+/** The maximum number of services per netif */
+#ifndef MDNS_MAX_SERVICES
+#define MDNS_MAX_SERVICES 1
+#endif
+
+/**
+ * MDNS_DEBUG: Enable debugging for multicast DNS.
+ */
+#ifndef MDNS_DEBUG
+#define MDNS_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * @}
+ */
+
+#endif /* LWIP_HDR_APPS_MDNS_OPTS_H */
+
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/mdns_priv.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/mdns_priv.h
new file mode 100644
index 0000000..8ee6db8
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/mdns_priv.h
@@ -0,0 +1,66 @@
+/**
+ * @file
+ * MDNS responder private definitions
+ */
+
+ /*
+ * Copyright (c) 2015 Verisure Innovation AB
+ * 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: Erik Ekman <erik@kryo.se>
+ *
+ */
+#ifndef LWIP_HDR_MDNS_PRIV_H
+#define LWIP_HDR_MDNS_PRIV_H
+
+#include "lwip/apps/mdns_opts.h"
+#include "lwip/pbuf.h"
+
+#if LWIP_MDNS_RESPONDER
+
+/* Domain struct and methods - visible for unit tests */
+
+#define MDNS_DOMAIN_MAXLEN 256
+#define MDNS_READNAME_ERROR 0xFFFF
+
+struct mdns_domain {
+ /* Encoded domain name */
+ u8_t name[MDNS_DOMAIN_MAXLEN];
+ /* Total length of domain name, including zero */
+ u16_t length;
+ /* Set if compression of this domain is not allowed */
+ u8_t skip_compression;
+};
+
+err_t mdns_domain_add_label(struct mdns_domain *domain, const char *label, u8_t len);
+u16_t mdns_readname(struct pbuf *p, u16_t offset, struct mdns_domain *domain);
+int mdns_domain_eq(struct mdns_domain *a, struct mdns_domain *b);
+u16_t mdns_compress_domain(struct pbuf *pbuf, u16_t *offset, struct mdns_domain *domain);
+
+#endif /* LWIP_MDNS_RESPONDER */
+
+#endif /* LWIP_HDR_MDNS_PRIV_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/mqtt.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/mqtt.h
new file mode 100644
index 0000000..34b230b
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/mqtt.h
@@ -0,0 +1,244 @@
+/**
+ * @file
+ * MQTT client
+ */
+
+/*
+ * Copyright (c) 2016 Erik Andersson
+ * 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: Erik Andersson
+ *
+ */
+#ifndef LWIP_HDR_APPS_MQTT_CLIENT_H
+#define LWIP_HDR_APPS_MQTT_CLIENT_H
+
+#include "lwip/apps/mqtt_opts.h"
+#include "lwip/err.h"
+#include "lwip/ip_addr.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct mqtt_client_t mqtt_client_t;
+
+/** @ingroup mqtt
+ * Default MQTT port */
+#define MQTT_PORT 1883
+
+/*---------------------------------------------------------------------------------------------- */
+/* Connection with server */
+
+/**
+ * @ingroup mqtt
+ * Client information and connection parameters */
+struct mqtt_connect_client_info_t {
+ /** Client identifier, must be set by caller */
+ const char *client_id;
+ /** User name and password, set to NULL if not used */
+ const char* client_user;
+ const char* client_pass;
+ /** keep alive time in seconds, 0 to disable keep alive functionality*/
+ u16_t keep_alive;
+ /** will topic, set to NULL if will is not to be used,
+ will_msg, will_qos and will retain are then ignored */
+ const char* will_topic;
+ const char* will_msg;
+ u8_t will_qos;
+ u8_t will_retain;
+};
+
+/**
+ * @ingroup mqtt
+ * Connection status codes */
+typedef enum
+{
+ MQTT_CONNECT_ACCEPTED = 0,
+ MQTT_CONNECT_REFUSED_PROTOCOL_VERSION = 1,
+ MQTT_CONNECT_REFUSED_IDENTIFIER = 2,
+ MQTT_CONNECT_REFUSED_SERVER = 3,
+ MQTT_CONNECT_REFUSED_USERNAME_PASS = 4,
+ MQTT_CONNECT_REFUSED_NOT_AUTHORIZED_ = 5,
+ MQTT_CONNECT_DISCONNECTED = 256,
+ MQTT_CONNECT_TIMEOUT = 257
+} mqtt_connection_status_t;
+
+/**
+ * @ingroup mqtt
+ * Function prototype for mqtt connection status callback. Called when
+ * client has connected to the server after initiating a mqtt connection attempt by
+ * calling mqtt_connect() or when connection is closed by server or an error
+ *
+ * @param client MQTT client itself
+ * @param arg Additional argument to pass to the callback function
+ * @param status Connect result code or disconnection notification @see mqtt_connection_status_t
+ *
+ */
+typedef void (*mqtt_connection_cb_t)(mqtt_client_t *client, void *arg, mqtt_connection_status_t status);
+
+
+/**
+ * @ingroup mqtt
+ * Data callback flags */
+enum {
+ /** Flag set when last fragment of data arrives in data callback */
+ MQTT_DATA_FLAG_LAST = 1
+};
+
+/**
+ * @ingroup mqtt
+ * Function prototype for MQTT incoming publish data callback function. Called when data
+ * arrives to a subscribed topic @see mqtt_subscribe
+ *
+ * @param arg Additional argument to pass to the callback function
+ * @param data User data, pointed object, data may not be referenced after callback return,
+ NULL is passed when all publish data are delivered
+ * @param len Length of publish data fragment
+ * @param flags MQTT_DATA_FLAG_LAST set when this call contains the last part of data from publish message
+ *
+ */
+typedef void (*mqtt_incoming_data_cb_t)(void *arg, const u8_t *data, u16_t len, u8_t flags);
+
+
+/**
+ * @ingroup mqtt
+ * Function prototype for MQTT incoming publish function. Called when an incoming publish
+ * arrives to a subscribed topic @see mqtt_subscribe
+ *
+ * @param arg Additional argument to pass to the callback function
+ * @param topic Zero terminated Topic text string, topic may not be referenced after callback return
+ * @param tot_len Total length of publish data, if set to 0 (no publish payload) data callback will not be invoked
+ */
+typedef void (*mqtt_incoming_publish_cb_t)(void *arg, const char *topic, u32_t tot_len);
+
+
+/**
+ * @ingroup mqtt
+ * Function prototype for mqtt request callback. Called when a subscribe, unsubscribe
+ * or publish request has completed
+ * @param arg Pointer to user data supplied when invoking request
+ * @param err ERR_OK on success
+ * ERR_TIMEOUT if no response was received within timeout,
+ * ERR_ABRT if (un)subscribe was denied
+ */
+typedef void (*mqtt_request_cb_t)(void *arg, err_t err);
+
+
+/**
+ * Pending request item, binds application callback to pending server requests
+ */
+struct mqtt_request_t
+{
+ /** Next item in list, NULL means this is the last in chain,
+ next pointing at itself means request is unallocated */
+ struct mqtt_request_t *next;
+ /** Callback to upper layer */
+ mqtt_request_cb_t cb;
+ void *arg;
+ /** MQTT packet identifier */
+ u16_t pkt_id;
+ /** Expire time relative to element before this */
+ u16_t timeout_diff;
+};
+
+/** Ring buffer */
+struct mqtt_ringbuf_t {
+ u16_t put;
+ u16_t get;
+ u8_t buf[MQTT_OUTPUT_RINGBUF_SIZE];
+};
+
+/** MQTT client */
+struct mqtt_client_t
+{
+ /** Timers and timeouts */
+ u16_t cyclic_tick;
+ u16_t keep_alive;
+ u16_t server_watchdog;
+ /** Packet identifier generator*/
+ u16_t pkt_id_seq;
+ /** Packet identifier of pending incoming publish */
+ u16_t inpub_pkt_id;
+ /** Connection state */
+ u8_t conn_state;
+ struct tcp_pcb *conn;
+ /** Connection callback */
+ void *connect_arg;
+ mqtt_connection_cb_t connect_cb;
+ /** Pending requests to server */
+ struct mqtt_request_t *pend_req_queue;
+ struct mqtt_request_t req_list[MQTT_REQ_MAX_IN_FLIGHT];
+ void *inpub_arg;
+ /** Incoming data callback */
+ mqtt_incoming_data_cb_t data_cb;
+ mqtt_incoming_publish_cb_t pub_cb;
+ /** Input */
+ u32_t msg_idx;
+ u8_t rx_buffer[MQTT_VAR_HEADER_BUFFER_LEN];
+ /** Output ring-buffer */
+ struct mqtt_ringbuf_t output;
+};
+
+
+/** Connect to server */
+err_t mqtt_client_connect(mqtt_client_t *client, const ip_addr_t *ipaddr, u16_t port, mqtt_connection_cb_t cb, void *arg,
+ const struct mqtt_connect_client_info_t *client_info);
+
+/** Disconnect from server */
+void mqtt_disconnect(mqtt_client_t *client);
+
+/** Create new client */
+mqtt_client_t *mqtt_client_new(void);
+
+/** Check connection status */
+u8_t mqtt_client_is_connected(mqtt_client_t *client);
+
+/** Set callback to call for incoming publish */
+void mqtt_set_inpub_callback(mqtt_client_t *client, mqtt_incoming_publish_cb_t,
+ mqtt_incoming_data_cb_t data_cb, void *arg);
+
+/** Common function for subscribe and unsubscribe */
+err_t mqtt_sub_unsub(mqtt_client_t *client, const char *topic, u8_t qos, mqtt_request_cb_t cb, void *arg, u8_t sub);
+
+/** @ingroup mqtt
+ *Subscribe to topic */
+#define mqtt_subscribe(client, topic, qos, cb, arg) mqtt_sub_unsub(client, topic, qos, cb, arg, 1)
+/** @ingroup mqtt
+ * Unsubscribe to topic */
+#define mqtt_unsubscribe(client, topic, cb, arg) mqtt_sub_unsub(client, topic, 0, cb, arg, 0)
+
+
+/** Publish data to topic */
+err_t mqtt_publish(mqtt_client_t *client, const char *topic, const void *payload, u16_t payload_length, u8_t qos, u8_t retain,
+ mqtt_request_cb_t cb, void *arg);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_APPS_MQTT_CLIENT_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/mqtt_opts.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/mqtt_opts.h
new file mode 100644
index 0000000..ffefacd
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/mqtt_opts.h
@@ -0,0 +1,103 @@
+/**
+ * @file
+ * MQTT client options
+ */
+
+/*
+ * Copyright (c) 2016 Erik Andersson
+ * 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: Erik Andersson
+ *
+ */
+#ifndef LWIP_HDR_APPS_MQTT_OPTS_H
+#define LWIP_HDR_APPS_MQTT_OPTS_H
+
+#include "lwip/opt.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @defgroup mqtt_opts Options
+ * @ingroup mqtt
+ * @{
+ */
+
+/**
+ * Output ring-buffer size, must be able to fit largest outgoing publish message topic+payloads
+ */
+#ifndef MQTT_OUTPUT_RINGBUF_SIZE
+#define MQTT_OUTPUT_RINGBUF_SIZE 256
+#endif
+
+/**
+ * Number of bytes in receive buffer, must be at least the size of the longest incoming topic + 8
+ * If one wants to avoid fragmented incoming publish, set length to max incoming topic length + max payload length + 8
+ */
+#ifndef MQTT_VAR_HEADER_BUFFER_LEN
+#define MQTT_VAR_HEADER_BUFFER_LEN 128
+#endif
+
+/**
+ * Maximum number of pending subscribe, unsubscribe and publish requests to server .
+ */
+#ifndef MQTT_REQ_MAX_IN_FLIGHT
+#define MQTT_REQ_MAX_IN_FLIGHT 4
+#endif
+
+/**
+ * Seconds between each cyclic timer call.
+ */
+#ifndef MQTT_CYCLIC_TIMER_INTERVAL
+#define MQTT_CYCLIC_TIMER_INTERVAL 5
+#endif
+
+/**
+ * Publish, subscribe and unsubscribe request timeout in seconds.
+ */
+#ifndef MQTT_REQ_TIMEOUT
+#define MQTT_REQ_TIMEOUT 30
+#endif
+
+/**
+ * Seconds for MQTT connect response timeout after sending connect request
+ */
+#ifndef MQTT_CONNECT_TIMOUT
+#define MQTT_CONNECT_TIMOUT 100
+#endif
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_APPS_MQTT_OPTS_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/netbiosns.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/netbiosns.h
new file mode 100644
index 0000000..c9f68d8
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/netbiosns.h
@@ -0,0 +1,43 @@
+/**
+ * @file
+ * NETBIOS name service responder
+ */
+
+/*
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ */
+#ifndef LWIP_HDR_APPS_NETBIOS_H
+#define LWIP_HDR_APPS_NETBIOS_H
+
+#include "lwip/apps/netbiosns_opts.h"
+
+void netbiosns_init(void);
+#ifndef NETBIOS_LWIP_NAME
+void netbiosns_set_name(const char* hostname);
+#endif
+void netbiosns_stop(void);
+
+#endif /* LWIP_HDR_APPS_NETBIOS_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/netbiosns_opts.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/netbiosns_opts.h
new file mode 100644
index 0000000..0909ef7
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/netbiosns_opts.h
@@ -0,0 +1,59 @@
+/**
+ * @file
+ * NETBIOS name service responder options
+ */
+
+/*
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ */
+#ifndef LWIP_HDR_APPS_NETBIOS_OPTS_H
+#define LWIP_HDR_APPS_NETBIOS_OPTS_H
+
+#include "lwip/opt.h"
+
+/**
+ * @defgroup netbiosns_opts Options
+ * @ingroup netbiosns
+ * @{
+ */
+
+/** NetBIOS name of lwip device
+ * This must be uppercase until NETBIOS_STRCMP() is defined to a string
+ * comparision function that is case insensitive.
+ * If you want to use the netif's hostname, use this (with LWIP_NETIF_HOSTNAME):
+ * (ip_current_netif() != NULL ? ip_current_netif()->hostname != NULL ? ip_current_netif()->hostname : "" : "")
+ *
+ * If this is not defined, netbiosns_set_name() can be called at runtime to change the name.
+ */
+#ifdef __DOXYGEN__
+#define NETBIOS_LWIP_NAME "NETBIOSLWIPDEV"
+#endif
+
+/**
+ * @}
+ */
+
+#endif /* LWIP_HDR_APPS_NETBIOS_OPTS_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/snmp.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/snmp.h
new file mode 100644
index 0000000..10e8ff4
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/snmp.h
@@ -0,0 +1,128 @@
+/**
+ * @file
+ * SNMP server main API - start and basic configuration
+ */
+
+/*
+ * Copyright (c) 2001, 2002 Leon Woestenberg <leon.woestenberg@axon.tv>
+ * Copyright (c) 2001, 2002 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Leon Woestenberg <leon.woestenberg@axon.tv>
+ * Martin Hentschel <info@cl-soft.de>
+ *
+ */
+#ifndef LWIP_HDR_APPS_SNMP_H
+#define LWIP_HDR_APPS_SNMP_H
+
+#include "lwip/apps/snmp_opts.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/err.h"
+#include "lwip/apps/snmp_core.h"
+
+/** SNMP variable binding descriptor (publically needed for traps) */
+struct snmp_varbind
+{
+ /** pointer to next varbind, NULL for last in list */
+ struct snmp_varbind *next;
+ /** pointer to previous varbind, NULL for first in list */
+ struct snmp_varbind *prev;
+
+ /** object identifier */
+ struct snmp_obj_id oid;
+
+ /** value ASN1 type */
+ u8_t type;
+ /** object value length */
+ u16_t value_len;
+ /** object value */
+ void *value;
+};
+
+/**
+ * @ingroup snmp_core
+ * Agent setup, start listening to port 161.
+ */
+void snmp_init(void);
+void snmp_set_mibs(const struct snmp_mib **mibs, u8_t num_mibs);
+
+void snmp_set_device_enterprise_oid(const struct snmp_obj_id* device_enterprise_oid);
+const struct snmp_obj_id* snmp_get_device_enterprise_oid(void);
+
+void snmp_trap_dst_enable(u8_t dst_idx, u8_t enable);
+void snmp_trap_dst_ip_set(u8_t dst_idx, const ip_addr_t *dst);
+
+/** Generic trap: cold start */
+#define SNMP_GENTRAP_COLDSTART 0
+/** Generic trap: warm start */
+#define SNMP_GENTRAP_WARMSTART 1
+/** Generic trap: link down */
+#define SNMP_GENTRAP_LINKDOWN 2
+/** Generic trap: link up */
+#define SNMP_GENTRAP_LINKUP 3
+/** Generic trap: authentication failure */
+#define SNMP_GENTRAP_AUTH_FAILURE 4
+/** Generic trap: EGP neighbor lost */
+#define SNMP_GENTRAP_EGP_NEIGHBOR_LOSS 5
+/** Generic trap: enterprise specific */
+#define SNMP_GENTRAP_ENTERPRISE_SPECIFIC 6
+
+err_t snmp_send_trap_generic(s32_t generic_trap);
+err_t snmp_send_trap_specific(s32_t specific_trap, struct snmp_varbind *varbinds);
+err_t snmp_send_trap(const struct snmp_obj_id* oid, s32_t generic_trap, s32_t specific_trap, struct snmp_varbind *varbinds);
+
+#define SNMP_AUTH_TRAPS_DISABLED 0
+#define SNMP_AUTH_TRAPS_ENABLED 1
+void snmp_set_auth_traps_enabled(u8_t enable);
+u8_t snmp_get_auth_traps_enabled(void);
+
+const char * snmp_get_community(void);
+const char * snmp_get_community_write(void);
+const char * snmp_get_community_trap(void);
+void snmp_set_community(const char * const community);
+void snmp_set_community_write(const char * const community);
+void snmp_set_community_trap(const char * const community);
+
+void snmp_coldstart_trap(void);
+void snmp_authfail_trap(void);
+
+typedef void (*snmp_write_callback_fct)(const u32_t* oid, u8_t oid_len, void* callback_arg);
+void snmp_set_write_callback(snmp_write_callback_fct write_callback, void* callback_arg);
+
+#endif /* LWIP_SNMP */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_APPS_SNMP_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/snmp_core.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/snmp_core.h
new file mode 100644
index 0000000..e781c53
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/snmp_core.h
@@ -0,0 +1,364 @@
+/**
+ * @file
+ * SNMP core API for implementing MIBs
+ */
+
+/*
+ * 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>
+ * Martin Hentschel <info@cl-soft.de>
+ */
+
+#ifndef LWIP_HDR_APPS_SNMP_CORE_H
+#define LWIP_HDR_APPS_SNMP_CORE_H
+
+#include "lwip/apps/snmp_opts.h"
+
+#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/ip_addr.h"
+#include "lwip/err.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* basic ASN1 defines */
+#define SNMP_ASN1_CLASS_UNIVERSAL 0x00
+#define SNMP_ASN1_CLASS_APPLICATION 0x40
+#define SNMP_ASN1_CLASS_CONTEXT 0x80
+#define SNMP_ASN1_CLASS_PRIVATE 0xC0
+
+#define SNMP_ASN1_CONTENTTYPE_PRIMITIVE 0x00
+#define SNMP_ASN1_CONTENTTYPE_CONSTRUCTED 0x20
+
+/* universal tags (from ASN.1 spec.) */
+#define SNMP_ASN1_UNIVERSAL_END_OF_CONTENT 0
+#define SNMP_ASN1_UNIVERSAL_INTEGER 2
+#define SNMP_ASN1_UNIVERSAL_OCTET_STRING 4
+#define SNMP_ASN1_UNIVERSAL_NULL 5
+#define SNMP_ASN1_UNIVERSAL_OBJECT_ID 6
+#define SNMP_ASN1_UNIVERSAL_SEQUENCE_OF 16
+
+/* application specific (SNMP) tags (from SNMPv2-SMI) */
+#define SNMP_ASN1_APPLICATION_IPADDR 0 /* [APPLICATION 0] IMPLICIT OCTET STRING (SIZE (4)) */
+#define SNMP_ASN1_APPLICATION_COUNTER 1 /* [APPLICATION 1] IMPLICIT INTEGER (0..4294967295) => u32_t */
+#define SNMP_ASN1_APPLICATION_GAUGE 2 /* [APPLICATION 2] IMPLICIT INTEGER (0..4294967295) => u32_t */
+#define SNMP_ASN1_APPLICATION_TIMETICKS 3 /* [APPLICATION 3] IMPLICIT INTEGER (0..4294967295) => u32_t */
+#define SNMP_ASN1_APPLICATION_OPAQUE 4 /* [APPLICATION 4] IMPLICIT OCTET STRING */
+#define SNMP_ASN1_APPLICATION_COUNTER64 6 /* [APPLICATION 6] IMPLICIT INTEGER (0..18446744073709551615) */
+
+/* context specific (SNMP) tags (from RFC 1905) */
+#define SNMP_ASN1_CONTEXT_VARBIND_NO_SUCH_INSTANCE 1
+
+/* full ASN1 type defines */
+#define SNMP_ASN1_TYPE_END_OF_CONTENT (SNMP_ASN1_CLASS_UNIVERSAL | SNMP_ASN1_CONTENTTYPE_PRIMITIVE | SNMP_ASN1_UNIVERSAL_END_OF_CONTENT)
+#define SNMP_ASN1_TYPE_INTEGER (SNMP_ASN1_CLASS_UNIVERSAL | SNMP_ASN1_CONTENTTYPE_PRIMITIVE | SNMP_ASN1_UNIVERSAL_INTEGER)
+#define SNMP_ASN1_TYPE_OCTET_STRING (SNMP_ASN1_CLASS_UNIVERSAL | SNMP_ASN1_CONTENTTYPE_PRIMITIVE | SNMP_ASN1_UNIVERSAL_OCTET_STRING)
+#define SNMP_ASN1_TYPE_NULL (SNMP_ASN1_CLASS_UNIVERSAL | SNMP_ASN1_CONTENTTYPE_PRIMITIVE | SNMP_ASN1_UNIVERSAL_NULL)
+#define SNMP_ASN1_TYPE_OBJECT_ID (SNMP_ASN1_CLASS_UNIVERSAL | SNMP_ASN1_CONTENTTYPE_PRIMITIVE | SNMP_ASN1_UNIVERSAL_OBJECT_ID)
+#define SNMP_ASN1_TYPE_SEQUENCE (SNMP_ASN1_CLASS_UNIVERSAL | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_UNIVERSAL_SEQUENCE_OF)
+#define SNMP_ASN1_TYPE_IPADDR (SNMP_ASN1_CLASS_APPLICATION | SNMP_ASN1_CONTENTTYPE_PRIMITIVE | SNMP_ASN1_APPLICATION_IPADDR)
+#define SNMP_ASN1_TYPE_IPADDRESS SNMP_ASN1_TYPE_IPADDR
+#define SNMP_ASN1_TYPE_COUNTER (SNMP_ASN1_CLASS_APPLICATION | SNMP_ASN1_CONTENTTYPE_PRIMITIVE | SNMP_ASN1_APPLICATION_COUNTER)
+#define SNMP_ASN1_TYPE_COUNTER32 SNMP_ASN1_TYPE_COUNTER
+#define SNMP_ASN1_TYPE_GAUGE (SNMP_ASN1_CLASS_APPLICATION | SNMP_ASN1_CONTENTTYPE_PRIMITIVE | SNMP_ASN1_APPLICATION_GAUGE)
+#define SNMP_ASN1_TYPE_GAUGE32 SNMP_ASN1_TYPE_GAUGE
+#define SNMP_ASN1_TYPE_UNSIGNED32 SNMP_ASN1_TYPE_GAUGE
+#define SNMP_ASN1_TYPE_TIMETICKS (SNMP_ASN1_CLASS_APPLICATION | SNMP_ASN1_CONTENTTYPE_PRIMITIVE | SNMP_ASN1_APPLICATION_TIMETICKS)
+#define SNMP_ASN1_TYPE_OPAQUE (SNMP_ASN1_CLASS_APPLICATION | SNMP_ASN1_CONTENTTYPE_PRIMITIVE | SNMP_ASN1_APPLICATION_OPAQUE)
+#define SNMP_ASN1_TYPE_COUNTER64 (SNMP_ASN1_CLASS_APPLICATION | SNMP_ASN1_CONTENTTYPE_PRIMITIVE | SNMP_ASN1_APPLICATION_COUNTER64)
+
+#define SNMP_VARBIND_EXCEPTION_OFFSET 0xF0
+#define SNMP_VARBIND_EXCEPTION_MASK 0x0F
+
+/** error codes predefined by SNMP prot. */
+typedef enum {
+ SNMP_ERR_NOERROR = 0,
+/*
+outdated v1 error codes. do not use anmore!
+#define SNMP_ERR_NOSUCHNAME 2 use SNMP_ERR_NOSUCHINSTANCE instead
+#define SNMP_ERR_BADVALUE 3 use SNMP_ERR_WRONGTYPE,SNMP_ERR_WRONGLENGTH,SNMP_ERR_WRONGENCODING or SNMP_ERR_WRONGVALUE instead
+#define SNMP_ERR_READONLY 4 use SNMP_ERR_NOTWRITABLE instead
+*/
+ SNMP_ERR_GENERROR = 5,
+ SNMP_ERR_NOACCESS = 6,
+ SNMP_ERR_WRONGTYPE = 7,
+ SNMP_ERR_WRONGLENGTH = 8,
+ SNMP_ERR_WRONGENCODING = 9,
+ SNMP_ERR_WRONGVALUE = 10,
+ SNMP_ERR_NOCREATION = 11,
+ SNMP_ERR_INCONSISTENTVALUE = 12,
+ SNMP_ERR_RESOURCEUNAVAILABLE = 13,
+ SNMP_ERR_COMMITFAILED = 14,
+ SNMP_ERR_UNDOFAILED = 15,
+ SNMP_ERR_NOTWRITABLE = 17,
+ SNMP_ERR_INCONSISTENTNAME = 18,
+
+ SNMP_ERR_NOSUCHINSTANCE = SNMP_VARBIND_EXCEPTION_OFFSET + SNMP_ASN1_CONTEXT_VARBIND_NO_SUCH_INSTANCE
+} snmp_err_t;
+
+/** internal object identifier representation */
+struct snmp_obj_id
+{
+ u8_t len;
+ u32_t id[SNMP_MAX_OBJ_ID_LEN];
+};
+
+struct snmp_obj_id_const_ref
+{
+ u8_t len;
+ const u32_t* id;
+};
+
+extern const struct snmp_obj_id_const_ref snmp_zero_dot_zero; /* administrative identifier from SNMPv2-SMI */
+
+/** SNMP variant value, used as reference in struct snmp_node_instance and table implementation */
+union snmp_variant_value
+{
+ void* ptr;
+ const void* const_ptr;
+ u32_t u32;
+ s32_t s32;
+};
+
+
+/**
+SNMP MIB node types
+ tree node is the only node the stack can process in order to walk the tree,
+ all other nodes are assumed to be leaf nodes.
+ This cannot be an enum because users may want to define their own node types.
+*/
+#define SNMP_NODE_TREE 0x00
+/* predefined leaf node types */
+#define SNMP_NODE_SCALAR 0x01
+#define SNMP_NODE_SCALAR_ARRAY 0x02
+#define SNMP_NODE_TABLE 0x03
+#define SNMP_NODE_THREADSYNC 0x04
+
+/** node "base class" layout, the mandatory fields for a node */
+struct snmp_node
+{
+ /** one out of SNMP_NODE_TREE or any leaf node type (like SNMP_NODE_SCALAR) */
+ u8_t node_type;
+ /** the number assigned to this node which used as part of the full OID */
+ u32_t oid;
+};
+
+/** SNMP node instance access types */
+typedef enum {
+ SNMP_NODE_INSTANCE_ACCESS_READ = 1,
+ SNMP_NODE_INSTANCE_ACCESS_WRITE = 2,
+ SNMP_NODE_INSTANCE_READ_ONLY = SNMP_NODE_INSTANCE_ACCESS_READ,
+ SNMP_NODE_INSTANCE_READ_WRITE = (SNMP_NODE_INSTANCE_ACCESS_READ | SNMP_NODE_INSTANCE_ACCESS_WRITE),
+ SNMP_NODE_INSTANCE_WRITE_ONLY = SNMP_NODE_INSTANCE_ACCESS_WRITE,
+ SNMP_NODE_INSTANCE_NOT_ACCESSIBLE = 0
+} snmp_access_t;
+
+struct snmp_node_instance;
+
+typedef s16_t (*node_instance_get_value_method)(struct snmp_node_instance*, void*);
+typedef snmp_err_t (*node_instance_set_test_method)(struct snmp_node_instance*, u16_t, void*);
+typedef snmp_err_t (*node_instance_set_value_method)(struct snmp_node_instance*, u16_t, void*);
+typedef void (*node_instance_release_method)(struct snmp_node_instance*);
+
+#define SNMP_GET_VALUE_RAW_DATA 0x8000
+
+/** SNMP node instance */
+struct snmp_node_instance
+{
+ /** prefilled with the node, get_instance() is called on; may be changed by user to any value to pass an arbitrary node between calls to get_instance() and get_value/test_value/set_value */
+ const struct snmp_node* node;
+ /** prefilled with the instance id requested; for get_instance() this is the exact oid requested; for get_next_instance() this is the relative starting point, stack expects relative oid of next node here */
+ struct snmp_obj_id instance_oid;
+
+ /** ASN type for this object (see snmp_asn1.h for definitions) */
+ u8_t asn1_type;
+ /** one out of instance access types defined above (SNMP_NODE_INSTANCE_READ_ONLY,...) */
+ snmp_access_t access;
+
+ /** returns object value for the given object identifier. Return values <0 to indicate an error */
+ node_instance_get_value_method get_value;
+ /** tests length and/or range BEFORE setting */
+ node_instance_set_test_method set_test;
+ /** sets object value, only called when set_test() was successful */
+ node_instance_set_value_method set_value;
+ /** called in any case when the instance is not required anymore by stack (useful for freeing memory allocated in get_instance/get_next_instance methods) */
+ node_instance_release_method release_instance;
+
+ /** reference to pass arbitrary value between calls to get_instance() and get_value/test_value/set_value */
+ union snmp_variant_value reference;
+ /** see reference (if reference is a pointer, the length of underlying data may be stored here or anything else) */
+ u32_t reference_len;
+};
+
+
+/** SNMP tree node */
+struct snmp_tree_node
+{
+ /** inherited "base class" members */
+ struct snmp_node node;
+ u16_t subnode_count;
+ const struct snmp_node* const *subnodes;
+};
+
+#define SNMP_CREATE_TREE_NODE(oid, subnodes) \
+ {{ SNMP_NODE_TREE, (oid) }, \
+ (u16_t)LWIP_ARRAYSIZE(subnodes), (subnodes) }
+
+#define SNMP_CREATE_EMPTY_TREE_NODE(oid) \
+ {{ SNMP_NODE_TREE, (oid) }, \
+ 0, NULL }
+
+/** SNMP leaf node */
+struct snmp_leaf_node
+{
+ /** inherited "base class" members */
+ struct snmp_node node;
+ snmp_err_t (*get_instance)(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance);
+ snmp_err_t (*get_next_instance)(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance);
+};
+
+/** represents a single mib with its base oid and root node */
+struct snmp_mib
+{
+ const u32_t *base_oid;
+ u8_t base_oid_len;
+ const struct snmp_node *root_node;
+};
+
+#define SNMP_MIB_CREATE(oid_list, root_node) { (oid_list), (u8_t)LWIP_ARRAYSIZE(oid_list), root_node }
+
+/** OID range structure */
+struct snmp_oid_range
+{
+ u32_t min;
+ u32_t max;
+};
+
+/** checks if incoming OID length and values are in allowed ranges */
+u8_t snmp_oid_in_range(const u32_t *oid_in, u8_t oid_len, const struct snmp_oid_range *oid_ranges, u8_t oid_ranges_len);
+
+typedef enum {
+ SNMP_NEXT_OID_STATUS_SUCCESS,
+ SNMP_NEXT_OID_STATUS_NO_MATCH,
+ SNMP_NEXT_OID_STATUS_BUF_TO_SMALL
+} snmp_next_oid_status_t;
+
+/** state for next_oid_init / next_oid_check functions */
+struct snmp_next_oid_state
+{
+ const u32_t* start_oid;
+ u8_t start_oid_len;
+
+ u32_t* next_oid;
+ u8_t next_oid_len;
+ u8_t next_oid_max_len;
+
+ snmp_next_oid_status_t status;
+ void* reference;
+};
+
+void snmp_next_oid_init(struct snmp_next_oid_state *state,
+ const u32_t *start_oid, u8_t start_oid_len,
+ u32_t *next_oid_buf, u8_t next_oid_max_len);
+u8_t snmp_next_oid_precheck(struct snmp_next_oid_state *state, const u32_t *oid, const u8_t oid_len);
+u8_t snmp_next_oid_check(struct snmp_next_oid_state *state, const u32_t *oid, const u8_t oid_len, void* reference);
+
+void snmp_oid_assign(struct snmp_obj_id* target, const u32_t *oid, u8_t oid_len);
+void snmp_oid_combine(struct snmp_obj_id* target, const u32_t *oid1, u8_t oid1_len, const u32_t *oid2, u8_t oid2_len);
+void snmp_oid_prefix(struct snmp_obj_id* target, const u32_t *oid, u8_t oid_len);
+void snmp_oid_append(struct snmp_obj_id* target, const u32_t *oid, u8_t oid_len);
+u8_t snmp_oid_equal(const u32_t *oid1, u8_t oid1_len, const u32_t *oid2, u8_t oid2_len);
+s8_t snmp_oid_compare(const u32_t *oid1, u8_t oid1_len, const u32_t *oid2, u8_t oid2_len);
+
+#if LWIP_IPV4
+u8_t snmp_oid_to_ip4(const u32_t *oid, ip4_addr_t *ip);
+void snmp_ip4_to_oid(const ip4_addr_t *ip, u32_t *oid);
+#endif /* LWIP_IPV4 */
+#if LWIP_IPV6
+u8_t snmp_oid_to_ip6(const u32_t *oid, ip6_addr_t *ip);
+void snmp_ip6_to_oid(const ip6_addr_t *ip, u32_t *oid);
+#endif /* LWIP_IPV6 */
+#if LWIP_IPV4 || LWIP_IPV6
+u8_t snmp_ip_to_oid(const ip_addr_t *ip, u32_t *oid);
+u8_t snmp_ip_port_to_oid(const ip_addr_t *ip, u16_t port, u32_t *oid);
+
+u8_t snmp_oid_to_ip(const u32_t *oid, u8_t oid_len, ip_addr_t *ip);
+u8_t snmp_oid_to_ip_port(const u32_t *oid, u8_t oid_len, ip_addr_t *ip, u16_t *port);
+#endif /* LWIP_IPV4 || LWIP_IPV6 */
+
+struct netif;
+u8_t netif_to_num(const struct netif *netif);
+
+snmp_err_t snmp_set_test_ok(struct snmp_node_instance* instance, u16_t value_len, void* value); /* generic function which can be used if test is always successful */
+
+err_t snmp_decode_bits(const u8_t *buf, u32_t buf_len, u32_t *bit_value);
+err_t snmp_decode_truthvalue(const s32_t *asn1_value, u8_t *bool_value);
+u8_t snmp_encode_bits(u8_t *buf, u32_t buf_len, u32_t bit_value, u8_t bit_count);
+u8_t snmp_encode_truthvalue(s32_t *asn1_value, u32_t bool_value);
+
+struct snmp_statistics
+{
+ u32_t inpkts;
+ u32_t outpkts;
+ u32_t inbadversions;
+ u32_t inbadcommunitynames;
+ u32_t inbadcommunityuses;
+ u32_t inasnparseerrs;
+ u32_t intoobigs;
+ u32_t innosuchnames;
+ u32_t inbadvalues;
+ u32_t inreadonlys;
+ u32_t ingenerrs;
+ u32_t intotalreqvars;
+ u32_t intotalsetvars;
+ u32_t ingetrequests;
+ u32_t ingetnexts;
+ u32_t insetrequests;
+ u32_t ingetresponses;
+ u32_t intraps;
+ u32_t outtoobigs;
+ u32_t outnosuchnames;
+ u32_t outbadvalues;
+ u32_t outgenerrs;
+ u32_t outgetrequests;
+ u32_t outgetnexts;
+ u32_t outsetrequests;
+ u32_t outgetresponses;
+ u32_t outtraps;
+};
+
+extern struct snmp_statistics snmp_stats;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_SNMP */
+
+#endif /* LWIP_HDR_APPS_SNMP_CORE_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/snmp_mib2.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/snmp_mib2.h
new file mode 100644
index 0000000..2f4a689
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/snmp_mib2.h
@@ -0,0 +1,78 @@
+/**
+ * @file
+ * SNMP MIB2 API
+ */
+
+/*
+ * 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: Dirk Ziegelmeier <dziegel@gmx.de>
+ *
+ */
+#ifndef LWIP_HDR_APPS_SNMP_MIB2_H
+#define LWIP_HDR_APPS_SNMP_MIB2_H
+
+#include "lwip/apps/snmp_opts.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
+#if SNMP_LWIP_MIB2
+
+#include "lwip/apps/snmp_core.h"
+
+extern const struct snmp_mib mib2;
+
+#if SNMP_USE_NETCONN
+#include "lwip/apps/snmp_threadsync.h"
+void snmp_mib2_lwip_synchronizer(snmp_threadsync_called_fn fn, void* arg);
+extern struct snmp_threadsync_instance snmp_mib2_lwip_locks;
+#endif
+
+#ifndef SNMP_SYSSERVICES
+#define SNMP_SYSSERVICES ((1 << 6) | (1 << 3) | ((IP_FORWARD) << 2))
+#endif
+
+void snmp_mib2_set_sysdescr(const u8_t* str, const u16_t* len); /* read-only be defintion */
+void snmp_mib2_set_syscontact(u8_t *ocstr, u16_t *ocstrlen, u16_t bufsize);
+void snmp_mib2_set_syscontact_readonly(const u8_t *ocstr, const u16_t *ocstrlen);
+void snmp_mib2_set_sysname(u8_t *ocstr, u16_t *ocstrlen, u16_t bufsize);
+void snmp_mib2_set_sysname_readonly(const u8_t *ocstr, const u16_t *ocstrlen);
+void snmp_mib2_set_syslocation(u8_t *ocstr, u16_t *ocstrlen, u16_t bufsize);
+void snmp_mib2_set_syslocation_readonly(const u8_t *ocstr, const u16_t *ocstrlen);
+
+#endif /* SNMP_LWIP_MIB2 */
+#endif /* LWIP_SNMP */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_APPS_SNMP_MIB2_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/snmp_opts.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/snmp_opts.h
new file mode 100644
index 0000000..6c9ba7b
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/snmp_opts.h
@@ -0,0 +1,293 @@
+/**
+ * @file
+ * SNMP server options list
+ */
+
+/*
+ * Copyright (c) 2015 Dirk Ziegelmeier
+ * 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: Dirk Ziegelmeier
+ *
+ */
+#ifndef LWIP_HDR_SNMP_OPTS_H
+#define LWIP_HDR_SNMP_OPTS_H
+
+#include "lwip/opt.h"
+
+/**
+ * @defgroup snmp_opts Options
+ * @ingroup snmp
+ * @{
+ */
+
+/**
+ * LWIP_SNMP==1: This enables the lwIP SNMP agent. UDP must be available
+ * for SNMP transport.
+ * If you want to use your own SNMP agent, leave this disabled.
+ * To integrate MIB2 of an external agent, you need to enable
+ * LWIP_MIB2_CALLBACKS and MIB2_STATS. This will give you the callbacks
+ * and statistics counters you need to get MIB2 working.
+ */
+#if !defined LWIP_SNMP || defined __DOXYGEN__
+#define LWIP_SNMP 0
+#endif
+
+/**
+ * SNMP_USE_NETCONN: Use netconn API instead of raw API.
+ * Makes SNMP agent run in a worker thread, so blocking operations
+ * can be done in MIB calls.
+ */
+#if !defined SNMP_USE_NETCONN || defined __DOXYGEN__
+#define SNMP_USE_NETCONN 0
+#endif
+
+/**
+ * SNMP_USE_RAW: Use raw API.
+ * SNMP agent does not run in a worker thread, so blocking operations
+ * should not be done in MIB calls.
+ */
+#if !defined SNMP_USE_RAW || defined __DOXYGEN__
+#define SNMP_USE_RAW 1
+#endif
+
+#if SNMP_USE_NETCONN && SNMP_USE_RAW
+#error SNMP stack can use only one of the APIs {raw, netconn}
+#endif
+
+#if LWIP_SNMP && !SNMP_USE_NETCONN && !SNMP_USE_RAW
+#error SNMP stack needs a receive API and UDP {raw, netconn}
+#endif
+
+#if SNMP_USE_NETCONN
+/**
+ * SNMP_STACK_SIZE: Stack size of SNMP netconn worker thread
+ */
+#if !defined SNMP_STACK_SIZE || defined __DOXYGEN__
+#define SNMP_STACK_SIZE DEFAULT_THREAD_STACKSIZE
+#endif
+
+/**
+ * SNMP_THREAD_PRIO: SNMP netconn worker thread priority
+ */
+#if !defined SNMP_THREAD_PRIO || defined __DOXYGEN__
+#define SNMP_THREAD_PRIO DEFAULT_THREAD_PRIO
+#endif
+#endif /* SNMP_USE_NETCONN */
+
+/**
+ * SNMP_TRAP_DESTINATIONS: Number of trap destinations. At least one trap
+ * destination is required
+ */
+#if !defined SNMP_TRAP_DESTINATIONS || defined __DOXYGEN__
+#define SNMP_TRAP_DESTINATIONS 1
+#endif
+
+/**
+ * Only allow SNMP write actions that are 'safe' (e.g. disabling netifs is not
+ * a safe action and disabled when SNMP_SAFE_REQUESTS = 1).
+ * Unsafe requests are disabled by default!
+ */
+#if !defined SNMP_SAFE_REQUESTS || defined __DOXYGEN__
+#define SNMP_SAFE_REQUESTS 1
+#endif
+
+/**
+ * The maximum length of strings used.
+ */
+#if !defined SNMP_MAX_OCTET_STRING_LEN || defined __DOXYGEN__
+#define SNMP_MAX_OCTET_STRING_LEN 127
+#endif
+
+/**
+ * The maximum number of Sub ID's inside an object identifier.
+ * Indirectly this also limits the maximum depth of SNMP tree.
+ */
+#if !defined SNMP_MAX_OBJ_ID_LEN || defined __DOXYGEN__
+#define SNMP_MAX_OBJ_ID_LEN 50
+#endif
+
+#if !defined SNMP_MAX_VALUE_SIZE || defined __DOXYGEN__
+/**
+ * The maximum size of a value.
+ */
+#define SNMP_MIN_VALUE_SIZE (2 * sizeof(u32_t*)) /* size required to store the basic types (8 bytes for counter64) */
+/**
+ * The minimum size of a value.
+ */
+#define SNMP_MAX_VALUE_SIZE LWIP_MAX(LWIP_MAX((SNMP_MAX_OCTET_STRING_LEN), sizeof(u32_t)*(SNMP_MAX_OBJ_ID_LEN)), SNMP_MIN_VALUE_SIZE)
+#endif
+
+/**
+ * The snmp read-access community. Used for write-access and traps, too
+ * unless SNMP_COMMUNITY_WRITE or SNMP_COMMUNITY_TRAP are enabled, respectively.
+ */
+#if !defined SNMP_COMMUNITY || defined __DOXYGEN__
+#define SNMP_COMMUNITY "public"
+#endif
+
+/**
+ * The snmp write-access community.
+ * Set this community to "" in order to disallow any write access.
+ */
+#if !defined SNMP_COMMUNITY_WRITE || defined __DOXYGEN__
+#define SNMP_COMMUNITY_WRITE "private"
+#endif
+
+/**
+ * The snmp community used for sending traps.
+ */
+#if !defined SNMP_COMMUNITY_TRAP || defined __DOXYGEN__
+#define SNMP_COMMUNITY_TRAP "public"
+#endif
+
+/**
+ * The maximum length of community string.
+ * If community names shall be adjusted at runtime via snmp_set_community() calls,
+ * enter here the possible maximum length (+1 for terminating null character).
+ */
+#if !defined SNMP_MAX_COMMUNITY_STR_LEN || defined __DOXYGEN__
+#define SNMP_MAX_COMMUNITY_STR_LEN LWIP_MAX(LWIP_MAX(sizeof(SNMP_COMMUNITY), sizeof(SNMP_COMMUNITY_WRITE)), sizeof(SNMP_COMMUNITY_TRAP))
+#endif
+
+/**
+ * The OID identifiying the device. This may be the enterprise OID itself or any OID located below it in tree.
+ */
+#if !defined SNMP_DEVICE_ENTERPRISE_OID || defined __DOXYGEN__
+#define SNMP_LWIP_ENTERPRISE_OID 26381
+/**
+ * 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!
+ * @note don't change this define, use snmp_set_device_enterprise_oid()
+ *
+ * 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_DEVICE_ENTERPRISE_OID {1, 3, 6, 1, 4, 1, SNMP_LWIP_ENTERPRISE_OID}
+/**
+ * Length of SNMP_DEVICE_ENTERPRISE_OID
+ */
+#define SNMP_DEVICE_ENTERPRISE_OID_LEN 7
+#endif
+
+/**
+ * SNMP_DEBUG: Enable debugging for SNMP messages.
+ */
+#if !defined SNMP_DEBUG || defined __DOXYGEN__
+#define SNMP_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * SNMP_MIB_DEBUG: Enable debugging for SNMP MIBs.
+ */
+#if !defined SNMP_MIB_DEBUG || defined __DOXYGEN__
+#define SNMP_MIB_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * Indicates if the MIB2 implementation of LWIP SNMP stack is used.
+ */
+#if !defined SNMP_LWIP_MIB2 || defined __DOXYGEN__
+#define SNMP_LWIP_MIB2 LWIP_SNMP
+#endif
+
+/**
+ * Value return for sysDesc field of MIB2.
+ */
+#if !defined SNMP_LWIP_MIB2_SYSDESC || defined __DOXYGEN__
+#define SNMP_LWIP_MIB2_SYSDESC "lwIP"
+#endif
+
+/**
+ * Value return for sysName field of MIB2.
+ * To make sysName field settable, call snmp_mib2_set_sysname() to provide the necessary buffers.
+ */
+#if !defined SNMP_LWIP_MIB2_SYSNAME || defined __DOXYGEN__
+#define SNMP_LWIP_MIB2_SYSNAME "FQDN-unk"
+#endif
+
+/**
+ * Value return for sysContact field of MIB2.
+ * To make sysContact field settable, call snmp_mib2_set_syscontact() to provide the necessary buffers.
+ */
+#if !defined SNMP_LWIP_MIB2_SYSCONTACT || defined __DOXYGEN__
+#define SNMP_LWIP_MIB2_SYSCONTACT ""
+#endif
+
+/**
+ * Value return for sysLocation field of MIB2.
+ * To make sysLocation field settable, call snmp_mib2_set_syslocation() to provide the necessary buffers.
+ */
+#if !defined SNMP_LWIP_MIB2_SYSLOCATION || defined __DOXYGEN__
+#define SNMP_LWIP_MIB2_SYSLOCATION ""
+#endif
+
+/**
+ * This value is used to limit the repetitions processed in GetBulk requests (value == 0 means no limitation).
+ * This may be useful to limit the load for a single request.
+ * According to SNMP RFC 1905 it is allowed to not return all requested variables from a GetBulk request if system load would be too high.
+ * so the effect is that the client will do more requests to gather all data.
+ * For the stack this could be useful in case that SNMP processing is done in TCP/IP thread. In this situation a request with many
+ * repetitions could block the thread for a longer time. Setting limit here will keep the stack more responsive.
+ */
+#if !defined SNMP_LWIP_GETBULK_MAX_REPETITIONS || defined __DOXYGEN__
+#define SNMP_LWIP_GETBULK_MAX_REPETITIONS 0
+#endif
+
+/**
+ * @}
+ */
+
+/*
+ ------------------------------------
+ ---------- SNMPv3 options ----------
+ ------------------------------------
+*/
+
+/**
+ * LWIP_SNMP_V3==1: This enables EXPERIMENTAL SNMPv3 support. LWIP_SNMP must
+ * also be enabled.
+ * THIS IS UNDER DEVELOPMENT AND SHOULD NOT BE ENABLED IN PRODUCTS.
+ */
+#ifndef LWIP_SNMP_V3
+#define LWIP_SNMP_V3 0
+#endif
+
+#ifndef LWIP_SNMP_V3_CRYPTO
+#define LWIP_SNMP_V3_CRYPTO LWIP_SNMP_V3
+#endif
+
+#ifndef LWIP_SNMP_V3_MBEDTLS
+#define LWIP_SNMP_V3_MBEDTLS LWIP_SNMP_V3
+#endif
+
+#endif /* LWIP_HDR_SNMP_OPTS_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/snmp_scalar.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/snmp_scalar.h
new file mode 100644
index 0000000..40a060c
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/snmp_scalar.h
@@ -0,0 +1,113 @@
+/**
+ * @file
+ * SNMP server MIB API to implement scalar nodes
+ */
+
+/*
+ * 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: Martin Hentschel <info@cl-soft.de>
+ *
+ */
+
+#ifndef LWIP_HDR_APPS_SNMP_SCALAR_H
+#define LWIP_HDR_APPS_SNMP_SCALAR_H
+
+#include "lwip/apps/snmp_opts.h"
+#include "lwip/apps/snmp_core.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
+
+/** basic scalar node */
+struct snmp_scalar_node
+{
+ /** inherited "base class" members */
+ struct snmp_leaf_node node;
+ u8_t asn1_type;
+ snmp_access_t access;
+ node_instance_get_value_method get_value;
+ node_instance_set_test_method set_test;
+ node_instance_set_value_method set_value;
+};
+
+
+snmp_err_t snmp_scalar_get_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance);
+snmp_err_t snmp_scalar_get_next_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance);
+
+#define SNMP_SCALAR_CREATE_NODE(oid, access, asn1_type, get_value_method, set_test_method, set_value_method) \
+ {{{ SNMP_NODE_SCALAR, (oid) }, \
+ snmp_scalar_get_instance, \
+ snmp_scalar_get_next_instance }, \
+ (asn1_type), (access), (get_value_method), (set_test_method), (set_value_method) }
+
+#define SNMP_SCALAR_CREATE_NODE_READONLY(oid, asn1_type, get_value_method) SNMP_SCALAR_CREATE_NODE(oid, SNMP_NODE_INSTANCE_READ_ONLY, asn1_type, get_value_method, NULL, NULL)
+
+/** scalar array node - a tree node which contains scalars only as children */
+struct snmp_scalar_array_node_def
+{
+ u32_t oid;
+ u8_t asn1_type;
+ snmp_access_t access;
+};
+
+typedef s16_t (*snmp_scalar_array_get_value_method)(const struct snmp_scalar_array_node_def*, void*);
+typedef snmp_err_t (*snmp_scalar_array_set_test_method)(const struct snmp_scalar_array_node_def*, u16_t, void*);
+typedef snmp_err_t (*snmp_scalar_array_set_value_method)(const struct snmp_scalar_array_node_def*, u16_t, void*);
+
+/** basic scalar array node */
+struct snmp_scalar_array_node
+{
+ /** inherited "base class" members */
+ struct snmp_leaf_node node;
+ u16_t array_node_count;
+ const struct snmp_scalar_array_node_def* array_nodes;
+ snmp_scalar_array_get_value_method get_value;
+ snmp_scalar_array_set_test_method set_test;
+ snmp_scalar_array_set_value_method set_value;
+};
+
+snmp_err_t snmp_scalar_array_get_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance);
+snmp_err_t snmp_scalar_array_get_next_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance);
+
+#define SNMP_SCALAR_CREATE_ARRAY_NODE(oid, array_nodes, get_value_method, set_test_method, set_value_method) \
+ {{{ SNMP_NODE_SCALAR_ARRAY, (oid) }, \
+ snmp_scalar_array_get_instance, \
+ snmp_scalar_array_get_next_instance }, \
+ (u16_t)LWIP_ARRAYSIZE(array_nodes), (array_nodes), (get_value_method), (set_test_method), (set_value_method) }
+
+#endif /* LWIP_SNMP */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_APPS_SNMP_SCALAR_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/snmp_table.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/snmp_table.h
new file mode 100644
index 0000000..4988b51
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/snmp_table.h
@@ -0,0 +1,134 @@
+/**
+ * @file
+ * SNMP server MIB API to implement table nodes
+ */
+
+/*
+ * 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: Martin Hentschel <info@cl-soft.de>
+ *
+ */
+
+#ifndef LWIP_HDR_APPS_SNMP_TABLE_H
+#define LWIP_HDR_APPS_SNMP_TABLE_H
+
+#include "lwip/apps/snmp_opts.h"
+#include "lwip/apps/snmp_core.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
+
+/** default (customizable) read/write table */
+struct snmp_table_col_def
+{
+ u32_t index;
+ u8_t asn1_type;
+ snmp_access_t access;
+};
+
+/** table node */
+struct snmp_table_node
+{
+ /** inherited "base class" members */
+ struct snmp_leaf_node node;
+ u16_t column_count;
+ const struct snmp_table_col_def* columns;
+ snmp_err_t (*get_cell_instance)(const u32_t* column, const u32_t* row_oid, u8_t row_oid_len, struct snmp_node_instance* cell_instance);
+ snmp_err_t (*get_next_cell_instance)(const u32_t* column, struct snmp_obj_id* row_oid, struct snmp_node_instance* cell_instance);
+ /** returns object value for the given object identifier */
+ node_instance_get_value_method get_value;
+ /** tests length and/or range BEFORE setting */
+ node_instance_set_test_method set_test;
+ /** sets object value, only called when set_test() was successful */
+ node_instance_set_value_method set_value;
+};
+
+snmp_err_t snmp_table_get_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance);
+snmp_err_t snmp_table_get_next_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance);
+
+#define SNMP_TABLE_CREATE(oid, columns, get_cell_instance_method, get_next_cell_instance_method, get_value_method, set_test_method, set_value_method) \
+ {{{ SNMP_NODE_TABLE, (oid) }, \
+ snmp_table_get_instance, \
+ snmp_table_get_next_instance }, \
+ (u16_t)LWIP_ARRAYSIZE(columns), (columns), \
+ (get_cell_instance_method), (get_next_cell_instance_method), \
+ (get_value_method), (set_test_method), (set_value_method)}
+
+#define SNMP_TABLE_GET_COLUMN_FROM_OID(oid) ((oid)[1]) /* first array value is (fixed) row entry (fixed to 1) and 2nd value is column, follow3ed by instance */
+
+
+/** simple read-only table */
+typedef enum {
+ SNMP_VARIANT_VALUE_TYPE_U32,
+ SNMP_VARIANT_VALUE_TYPE_S32,
+ SNMP_VARIANT_VALUE_TYPE_PTR,
+ SNMP_VARIANT_VALUE_TYPE_CONST_PTR
+} snmp_table_column_data_type_t;
+
+struct snmp_table_simple_col_def
+{
+ u32_t index;
+ u8_t asn1_type;
+ snmp_table_column_data_type_t data_type; /* depending of what union member is used to store the value*/
+};
+
+/** simple read-only table node */
+struct snmp_table_simple_node
+{
+ /* inherited "base class" members */
+ struct snmp_leaf_node node;
+ u16_t column_count;
+ const struct snmp_table_simple_col_def* columns;
+ snmp_err_t (*get_cell_value)(const u32_t* column, const u32_t* row_oid, u8_t row_oid_len, union snmp_variant_value* value, u32_t* value_len);
+ snmp_err_t (*get_next_cell_instance_and_value)(const u32_t* column, struct snmp_obj_id* row_oid, union snmp_variant_value* value, u32_t* value_len);
+};
+
+snmp_err_t snmp_table_simple_get_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance);
+snmp_err_t snmp_table_simple_get_next_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance);
+
+#define SNMP_TABLE_CREATE_SIMPLE(oid, columns, get_cell_value_method, get_next_cell_instance_and_value_method) \
+ {{{ SNMP_NODE_TABLE, (oid) }, \
+ snmp_table_simple_get_instance, \
+ snmp_table_simple_get_next_instance }, \
+ (u16_t)LWIP_ARRAYSIZE(columns), (columns), (get_cell_value_method), (get_next_cell_instance_and_value_method) }
+
+s16_t snmp_table_extract_value_from_s32ref(struct snmp_node_instance* instance, void* value);
+s16_t snmp_table_extract_value_from_u32ref(struct snmp_node_instance* instance, void* value);
+s16_t snmp_table_extract_value_from_refconstptr(struct snmp_node_instance* instance, void* value);
+
+#endif /* LWIP_SNMP */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_APPS_SNMP_TABLE_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/snmp_threadsync.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/snmp_threadsync.h
new file mode 100644
index 0000000..a25dbf2
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/snmp_threadsync.h
@@ -0,0 +1,114 @@
+/**
+ * @file
+ * SNMP server MIB API to implement thread synchronization
+ */
+
+/*
+ * 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: Dirk Ziegelmeier <dziegel@gmx.de>
+ *
+ */
+
+#ifndef LWIP_HDR_APPS_SNMP_THREADSYNC_H
+#define LWIP_HDR_APPS_SNMP_THREADSYNC_H
+
+#include "lwip/apps/snmp_opts.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/apps/snmp_core.h"
+#include "lwip/sys.h"
+
+typedef void (*snmp_threadsync_called_fn)(void* arg);
+typedef void (*snmp_threadsync_synchronizer_fn)(snmp_threadsync_called_fn fn, void* arg);
+
+
+/** Thread sync runtime data. For internal usage only. */
+struct threadsync_data
+{
+ union {
+ snmp_err_t err;
+ s16_t s16;
+ } retval;
+ union {
+ const u32_t *root_oid;
+ void *value;
+ } arg1;
+ union {
+ u8_t root_oid_len;
+ u16_t len;
+ } arg2;
+ const struct snmp_threadsync_node *threadsync_node;
+ struct snmp_node_instance proxy_instance;
+};
+
+/** Thread sync instance. Needed EXCATLY once for every thread to be synced into. */
+struct snmp_threadsync_instance
+{
+ sys_sem_t sem;
+ sys_mutex_t sem_usage_mutex;
+ snmp_threadsync_synchronizer_fn sync_fn;
+ struct threadsync_data data;
+};
+
+/** SNMP thread sync proxy leaf node */
+struct snmp_threadsync_node
+{
+ /* inherited "base class" members */
+ struct snmp_leaf_node node;
+
+ const struct snmp_leaf_node *target;
+ struct snmp_threadsync_instance *instance;
+};
+
+snmp_err_t snmp_threadsync_get_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance);
+snmp_err_t snmp_threadsync_get_next_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance);
+
+/** Create thread sync proxy node */
+#define SNMP_CREATE_THREAD_SYNC_NODE(oid, target_leaf_node, threadsync_instance) \
+ {{{ SNMP_NODE_THREADSYNC, (oid) }, \
+ snmp_threadsync_get_instance, \
+ snmp_threadsync_get_next_instance }, \
+ (target_leaf_node), \
+ (threadsync_instance) }
+
+/** Create thread sync instance data */
+void snmp_threadsync_init(struct snmp_threadsync_instance *instance, snmp_threadsync_synchronizer_fn sync_fn);
+
+#endif /* LWIP_SNMP */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_APPS_SNMP_THREADSYNC_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/snmpv3.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/snmpv3.h
new file mode 100644
index 0000000..c99fed4
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/snmpv3.h
@@ -0,0 +1,90 @@
+/**
+ * @file
+ * Additional SNMPv3 functionality RFC3414 and RFC3826.
+ */
+
+/*
+ * Copyright (c) 2016 Elias Oenal.
+ * 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: Elias Oenal <lwip@eliasoenal.com>
+ */
+
+#ifndef LWIP_HDR_APPS_SNMP_V3_H
+#define LWIP_HDR_APPS_SNMP_V3_H
+
+#include "lwip/apps/snmp_opts.h"
+#include "lwip/err.h"
+
+#if LWIP_SNMP && LWIP_SNMP_V3
+
+#define SNMP_V3_AUTH_ALGO_INVAL 0
+#define SNMP_V3_AUTH_ALGO_MD5 1
+#define SNMP_V3_AUTH_ALGO_SHA 2
+
+#define SNMP_V3_PRIV_ALGO_INVAL 0
+#define SNMP_V3_PRIV_ALGO_DES 1
+#define SNMP_V3_PRIV_ALGO_AES 2
+
+#define SNMP_V3_PRIV_MODE_DECRYPT 0
+#define SNMP_V3_PRIV_MODE_ENCRYPT 1
+
+/*
+ * The following callback functions must be implemented by the application.
+ * There is a dummy implementation in snmpv3_dummy.c.
+ */
+
+void snmpv3_get_engine_id(const char **id, u8_t *len);
+err_t snmpv3_set_engine_id(const char* id, u8_t len);
+
+u32_t snmpv3_get_engine_boots(void);
+void snmpv3_set_engine_boots(u32_t boots);
+
+u32_t snmpv3_get_engine_time(void);
+void snmpv3_reset_engine_time(void);
+
+err_t snmpv3_get_user(const char* username, u8_t *auth_algo, u8_t *auth_key, u8_t *priv_algo, u8_t *priv_key);
+
+/* The following functions are provided by the SNMPv3 agent */
+
+void snmpv3_engine_id_changed(void);
+
+void snmpv3_password_to_key_md5(
+ const u8_t *password, /* IN */
+ u8_t passwordlen, /* IN */
+ const u8_t *engineID, /* IN - pointer to snmpEngineID */
+ u8_t engineLength, /* IN - length of snmpEngineID */
+ u8_t *key); /* OUT - pointer to caller 16-octet buffer */
+
+void snmpv3_password_to_key_sha(
+ const u8_t *password, /* IN */
+ u8_t passwordlen, /* IN */
+ const u8_t *engineID, /* IN - pointer to snmpEngineID */
+ u8_t engineLength, /* IN - length of snmpEngineID */
+ u8_t *key); /* OUT - pointer to caller 20-octet buffer */
+
+#endif
+
+#endif /* LWIP_HDR_APPS_SNMP_V3_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/sntp.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/sntp.h
new file mode 100644
index 0000000..40df9cc
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/sntp.h
@@ -0,0 +1,76 @@
+/**
+ * @file
+ * SNTP client API
+ */
+
+/*
+ * Copyright (c) 2007-2009 Frédéric Bernon, Simon Goldschmidt
+ * 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: Frédéric Bernon, Simon Goldschmidt
+ *
+ */
+#ifndef LWIP_HDR_APPS_SNTP_H
+#define LWIP_HDR_APPS_SNTP_H
+
+#include "lwip/apps/sntp_opts.h"
+#include "lwip/ip_addr.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* SNTP operating modes: default is to poll using unicast.
+ The mode has to be set before calling sntp_init(). */
+#define SNTP_OPMODE_POLL 0
+#define SNTP_OPMODE_LISTENONLY 1
+void sntp_setoperatingmode(u8_t operating_mode);
+u8_t sntp_getoperatingmode(void);
+
+void sntp_init(void);
+void sntp_stop(void);
+u8_t sntp_enabled(void);
+
+void sntp_setserver(u8_t idx, const ip_addr_t *addr);
+const ip_addr_t* sntp_getserver(u8_t idx);
+
+#if SNTP_SERVER_DNS
+void sntp_setservername(u8_t idx, char *server);
+char *sntp_getservername(u8_t idx);
+#endif /* SNTP_SERVER_DNS */
+
+#if SNTP_GET_SERVERS_FROM_DHCP
+void sntp_servermode_dhcp(int set_servers_from_dhcp);
+#else /* SNTP_GET_SERVERS_FROM_DHCP */
+#define sntp_servermode_dhcp(x)
+#endif /* SNTP_GET_SERVERS_FROM_DHCP */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_APPS_SNTP_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/sntp_opts.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/sntp_opts.h
new file mode 100644
index 0000000..f3651f9
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/sntp_opts.h
@@ -0,0 +1,173 @@
+/**
+ * @file
+ * SNTP client options list
+ */
+
+/*
+ * Copyright (c) 2007-2009 Frédéric Bernon, Simon Goldschmidt
+ * 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: Frédéric Bernon, Simon Goldschmidt
+ *
+ */
+#ifndef LWIP_HDR_APPS_SNTP_OPTS_H
+#define LWIP_HDR_APPS_SNTP_OPTS_H
+
+#include "lwip/opt.h"
+
+/**
+ * @defgroup sntp_opts Options
+ * @ingroup sntp
+ * @{
+ */
+
+/** SNTP macro to change system time in seconds
+ * Define SNTP_SET_SYSTEM_TIME_US(sec, us) to set the time in microseconds instead of this one
+ * if you need the additional precision.
+ */
+#if !defined SNTP_SET_SYSTEM_TIME || defined __DOXYGEN__
+#define SNTP_SET_SYSTEM_TIME(sec) LWIP_UNUSED_ARG(sec)
+#endif
+
+/** The maximum number of SNTP servers that can be set */
+#if !defined SNTP_MAX_SERVERS || defined __DOXYGEN__
+#define SNTP_MAX_SERVERS LWIP_DHCP_MAX_NTP_SERVERS
+#endif
+
+/** Set this to 1 to implement the callback function called by dhcp when
+ * NTP servers are received. */
+#if !defined SNTP_GET_SERVERS_FROM_DHCP || defined __DOXYGEN__
+#define SNTP_GET_SERVERS_FROM_DHCP LWIP_DHCP_GET_NTP_SRV
+#endif
+
+/** Set this to 1 to support DNS names (or IP address strings) to set sntp servers
+ * One server address/name can be defined as default if SNTP_SERVER_DNS == 1:
+ * \#define SNTP_SERVER_ADDRESS "pool.ntp.org"
+ */
+#if !defined SNTP_SERVER_DNS || defined __DOXYGEN__
+#define SNTP_SERVER_DNS 0
+#endif
+
+/**
+ * SNTP_DEBUG: Enable debugging for SNTP.
+ */
+#if !defined SNTP_DEBUG || defined __DOXYGEN__
+#define SNTP_DEBUG LWIP_DBG_OFF
+#endif
+
+/** SNTP server port */
+#if !defined SNTP_PORT || defined __DOXYGEN__
+#define SNTP_PORT 123
+#endif
+
+/** Set this to 1 to allow config of SNTP server(s) by DNS name */
+#if !defined SNTP_SERVER_DNS || defined __DOXYGEN__
+#define SNTP_SERVER_DNS 0
+#endif
+
+/** Sanity check:
+ * Define this to
+ * - 0 to turn off sanity checks (default; smaller code)
+ * - >= 1 to check address and port of the response packet to ensure the
+ * response comes from the server we sent the request to.
+ * - >= 2 to check returned Originate Timestamp against Transmit Timestamp
+ * sent to the server (to ensure response to older request).
+ * - >= 3 @todo: discard reply if any of the LI, Stratum, or Transmit Timestamp
+ * fields is 0 or the Mode field is not 4 (unicast) or 5 (broadcast).
+ * - >= 4 @todo: to check that the Root Delay and Root Dispersion fields are each
+ * greater than or equal to 0 and less than infinity, where infinity is
+ * currently a cozy number like one second. This check avoids using a
+ * server whose synchronization source has expired for a very long time.
+ */
+#if !defined SNTP_CHECK_RESPONSE || defined __DOXYGEN__
+#define SNTP_CHECK_RESPONSE 0
+#endif
+
+/** According to the RFC, this shall be a random delay
+ * between 1 and 5 minutes (in milliseconds) to prevent load peaks.
+ * This can be defined to a random generation function,
+ * which must return the delay in milliseconds as u32_t.
+ * Turned off by default.
+ */
+#if !defined SNTP_STARTUP_DELAY || defined __DOXYGEN__
+#define SNTP_STARTUP_DELAY 0
+#endif
+
+/** If you want the startup delay to be a function, define this
+ * to a function (including the brackets) and define SNTP_STARTUP_DELAY to 1.
+ */
+#if !defined SNTP_STARTUP_DELAY_FUNC || defined __DOXYGEN__
+#define SNTP_STARTUP_DELAY_FUNC SNTP_STARTUP_DELAY
+#endif
+
+/** SNTP receive timeout - in milliseconds
+ * Also used as retry timeout - this shouldn't be too low.
+ * Default is 3 seconds.
+ */
+#if !defined SNTP_RECV_TIMEOUT || defined __DOXYGEN__
+#define SNTP_RECV_TIMEOUT 3000
+#endif
+
+/** SNTP update delay - in milliseconds
+ * Default is 1 hour. Must not be beolw 15 seconds by specification (i.e. 15000)
+ */
+#if !defined SNTP_UPDATE_DELAY || defined __DOXYGEN__
+#define SNTP_UPDATE_DELAY 3600000
+#endif
+
+/** SNTP macro to get system time, used with SNTP_CHECK_RESPONSE >= 2
+ * to send in request and compare in response.
+ */
+#if !defined SNTP_GET_SYSTEM_TIME || defined __DOXYGEN__
+#define SNTP_GET_SYSTEM_TIME(sec, us) do { (sec) = 0; (us) = 0; } while(0)
+#endif
+
+/** Default retry timeout (in milliseconds) if the response
+ * received is invalid.
+ * This is doubled with each retry until SNTP_RETRY_TIMEOUT_MAX is reached.
+ */
+#if !defined SNTP_RETRY_TIMEOUT || defined __DOXYGEN__
+#define SNTP_RETRY_TIMEOUT SNTP_RECV_TIMEOUT
+#endif
+
+/** Maximum retry timeout (in milliseconds). */
+#if !defined SNTP_RETRY_TIMEOUT_MAX || defined __DOXYGEN__
+#define SNTP_RETRY_TIMEOUT_MAX (SNTP_RETRY_TIMEOUT * 10)
+#endif
+
+/** Increase retry timeout with every retry sent
+ * Default is on to conform to RFC.
+ */
+#if !defined SNTP_RETRY_TIMEOUT_EXP || defined __DOXYGEN__
+#define SNTP_RETRY_TIMEOUT_EXP 1
+#endif
+
+/**
+ * @}
+ */
+
+#endif /* LWIP_HDR_APPS_SNTP_OPTS_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/tftp_opts.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/tftp_opts.h
new file mode 100644
index 0000000..6968a80
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/tftp_opts.h
@@ -0,0 +1,105 @@
+/****************************************************************//**
+ *
+ * @file tftp_opts.h
+ *
+ * @author Logan Gunthorpe <logang@deltatee.com>
+ *
+ * @brief Trivial File Transfer Protocol (RFC 1350) implementation options
+ *
+ * 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 LWIP_HDR_APPS_TFTP_OPTS_H
+#define LWIP_HDR_APPS_TFTP_OPTS_H
+
+#include "lwip/opt.h"
+
+/**
+ * @defgroup tftp_opts Options
+ * @ingroup tftp
+ * @{
+ */
+
+/**
+ * Enable TFTP debug messages
+ */
+#if !defined TFTP_DEBUG || defined __DOXYGEN__
+#define TFTP_DEBUG LWIP_DBG_ON
+#endif
+
+/**
+ * TFTP server port
+ */
+#if !defined TFTP_PORT || defined __DOXYGEN__
+#define TFTP_PORT 69
+#endif
+
+/**
+ * TFTP timeout
+ */
+#if !defined TFTP_TIMEOUT_MSECS || defined __DOXYGEN__
+#define TFTP_TIMEOUT_MSECS 10000
+#endif
+
+/**
+ * Max. number of retries when a file is read from server
+ */
+#if !defined TFTP_MAX_RETRIES || defined __DOXYGEN__
+#define TFTP_MAX_RETRIES 5
+#endif
+
+/**
+ * TFTP timer cyclic interval
+ */
+#if !defined TFTP_TIMER_MSECS || defined __DOXYGEN__
+#define TFTP_TIMER_MSECS 50
+#endif
+
+/**
+ * Max. length of TFTP filename
+ */
+#if !defined TFTP_MAX_FILENAME_LEN || defined __DOXYGEN__
+#define TFTP_MAX_FILENAME_LEN 20
+#endif
+
+/**
+ * Max. length of TFTP mode
+ */
+#if !defined TFTP_MAX_MODE_LEN || defined __DOXYGEN__
+#define TFTP_MAX_MODE_LEN 7
+#endif
+
+/**
+ * @}
+ */
+
+#endif /* LWIP_HDR_APPS_TFTP_OPTS_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/tftp_server.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/tftp_server.h
new file mode 100644
index 0000000..3fbe701
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/apps/tftp_server.h
@@ -0,0 +1,94 @@
+/****************************************************************//**
+ *
+ * @file tftp_server.h
+ *
+ * @author Logan Gunthorpe <logang@deltatee.com>
+ *
+ * @brief Trivial File Transfer Protocol (RFC 1350)
+ *
+ * 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 LWIP_HDR_APPS_TFTP_SERVER_H
+#define LWIP_HDR_APPS_TFTP_SERVER_H
+
+#include "lwip/apps/tftp_opts.h"
+#include "lwip/err.h"
+#include "lwip/pbuf.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** @ingroup tftp
+ * TFTP context containing callback functions for TFTP transfers
+ */
+struct tftp_context {
+ /**
+ * Open file for read/write.
+ * @param fname Filename
+ * @param mode Mode string from TFTP RFC 1350 (netascii, octet, mail)
+ * @param write Flag indicating read (0) or write (!= 0) access
+ * @returns File handle supplied to other functions
+ */
+ void* (*open)(const char* fname, const char* mode, u8_t write);
+ /**
+ * Close file handle
+ * @param handle File handle returned by open()
+ */
+ void (*close)(void* handle);
+ /**
+ * Read from file
+ * @param handle File handle returned by open()
+ * @param buf Target buffer to copy read data to
+ * @param bytes Number of bytes to copy to buf
+ * @returns &gt;= 0: Success; &lt; 0: Error
+ */
+ int (*read)(void* handle, void* buf, int bytes);
+ /**
+ * Write to file
+ * @param handle File handle returned by open()
+ * @param pbuf PBUF adjusted such that payload pointer points
+ * to the beginning of write data. In other words,
+ * TFTP headers are stripped off.
+ * @returns &gt;= 0: Success; &lt; 0: Error
+ */
+ int (*write)(void* handle, struct pbuf* p);
+};
+
+err_t tftp_init(const struct tftp_context* ctx);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_APPS_TFTP_SERVER_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/arch.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/arch.h
new file mode 100644
index 0000000..55714e1
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/arch.h
@@ -0,0 +1,319 @@
+/**
+ * @file
+ * Support for different processor and compiler architectures
+ */
+
+/*
+ * 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_HDR_ARCH_H
+#define LWIP_HDR_ARCH_H
+
+#ifndef LITTLE_ENDIAN
+#define LITTLE_ENDIAN 1234
+#endif
+
+#ifndef BIG_ENDIAN
+#define BIG_ENDIAN 4321
+#endif
+
+#include "arch/cc.h"
+
+/**
+ * @defgroup compiler_abstraction Compiler/platform abstraction
+ * @ingroup sys_layer
+ * All defines related to this section must not be placed in lwipopts.h,
+ * but in arch/cc.h!
+ * These options cannot be \#defined in lwipopts.h since they are not options
+ * of lwIP itself, but options of the lwIP port to your system.
+ * @{
+ */
+
+/** Define the byte order of the system.
+ * Needed for conversion of network data to host byte order.
+ * Allowed values: LITTLE_ENDIAN and BIG_ENDIAN
+ */
+#ifndef BYTE_ORDER
+#define BYTE_ORDER LITTLE_ENDIAN
+#endif
+
+/** Define random number generator function of your system */
+#ifdef __DOXYGEN__
+#define LWIP_RAND() ((u32_t)rand())
+#endif
+
+/** Platform specific diagnostic output.\n
+ * Note the default implementation pulls in printf, which may
+ * in turn pull in a lot of standard libary code. In resource-constrained
+ * systems, this should be defined to something less resource-consuming.
+ */
+#ifndef LWIP_PLATFORM_DIAG
+#define LWIP_PLATFORM_DIAG(x) do {printf x;} while(0)
+#include <stdio.h>
+#include <stdlib.h>
+#endif
+
+/** Platform specific assertion handling.\n
+ * Note the default implementation pulls in printf, fflush and abort, which may
+ * in turn pull in a lot of standard libary code. In resource-constrained
+ * systems, this should be defined to something less resource-consuming.
+ */
+#ifndef LWIP_PLATFORM_ASSERT
+#define LWIP_PLATFORM_ASSERT(x) do {printf("Assertion \"%s\" failed at line %d in %s\n", \
+ x, __LINE__, __FILE__); fflush(NULL); abort();} while(0)
+#include <stdio.h>
+#include <stdlib.h>
+#endif
+
+/** Define this to 1 in arch/cc.h of your port if you do not want to
+ * include stddef.h header to get size_t. You need to typedef size_t
+ * by yourself in this case.
+ */
+#ifndef LWIP_NO_STDDEF_H
+#define LWIP_NO_STDDEF_H 0
+#endif
+
+#if !LWIP_NO_STDDEF_H
+#include <stddef.h> /* for size_t */
+#endif
+
+/** Define this to 1 in arch/cc.h of your port if your compiler does not provide
+ * the stdint.h header. You need to typedef the generic types listed in
+ * lwip/arch.h yourself in this case (u8_t, u16_t...).
+ */
+#ifndef LWIP_NO_STDINT_H
+#define LWIP_NO_STDINT_H 0
+#endif
+
+/* Define generic types used in lwIP */
+#if !LWIP_NO_STDINT_H
+#include <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;
+#endif
+
+/** Define this to 1 in arch/cc.h of your port if your compiler does not provide
+ * the inttypes.h header. You need to define the format strings listed in
+ * lwip/arch.h yourself in this case (X8_F, U16_F...).
+ */
+#ifndef LWIP_NO_INTTYPES_H
+#define LWIP_NO_INTTYPES_H 0
+#endif
+
+/* Define (sn)printf formatters for these lwIP types */
+#if !LWIP_NO_INTTYPES_H
+#include <inttypes.h>
+#ifndef X8_F
+#define X8_F "02" PRIx8
+#endif
+#ifndef U16_F
+#define U16_F PRIu16
+#endif
+#ifndef S16_F
+#define S16_F PRId16
+#endif
+#ifndef X16_F
+#define X16_F PRIx16
+#endif
+#ifndef U32_F
+#define U32_F PRIu32
+#endif
+#ifndef S32_F
+#define S32_F PRId32
+#endif
+#ifndef X32_F
+#define X32_F PRIx32
+#endif
+#ifndef SZT_F
+#define SZT_F PRIuPTR
+#endif
+#endif
+
+/** Define this to 1 in arch/cc.h of your port if your compiler does not provide
+ * the limits.h header. You need to define the type limits yourself in this case
+ * (e.g. INT_MAX).
+ */
+#ifndef LWIP_NO_LIMITS_H
+#define LWIP_NO_LIMITS_H 0
+#endif
+
+/* Include limits.h? */
+#if !LWIP_NO_LIMITS_H
+#include <limits.h>
+#endif
+
+/** C++ const_cast<target_type>(val) equivalent to remove constness from a value (GCC -Wcast-qual) */
+#ifndef LWIP_CONST_CAST
+#define LWIP_CONST_CAST(target_type, val) ((target_type)((ptrdiff_t)val))
+#endif
+
+/** Get rid of alignment cast warnings (GCC -Wcast-align) */
+#ifndef LWIP_ALIGNMENT_CAST
+#define LWIP_ALIGNMENT_CAST(target_type, val) LWIP_CONST_CAST(target_type, val)
+#endif
+
+/** Get rid of warnings related to pointer-to-numeric and vice-versa casts,
+ * e.g. "conversion from 'u8_t' to 'void *' of greater size"
+ */
+#ifndef LWIP_PTR_NUMERIC_CAST
+#define LWIP_PTR_NUMERIC_CAST(target_type, val) LWIP_CONST_CAST(target_type, val)
+#endif
+
+/** Allocates a memory buffer of specified size that is of sufficient size to align
+ * its start address using LWIP_MEM_ALIGN.
+ * You can declare your own version here e.g. to enforce alignment without adding
+ * trailing padding bytes (see LWIP_MEM_ALIGN_BUFFER) or your own section placement
+ * requirements.\n
+ * e.g. if you use gcc and need 32 bit alignment:\n
+ * \#define LWIP_DECLARE_MEMORY_ALIGNED(variable_name, size) u8_t variable_name[size] \_\_attribute\_\_((aligned(4)))\n
+ * or more portable:\n
+ * \#define LWIP_DECLARE_MEMORY_ALIGNED(variable_name, size) u32_t variable_name[(size + sizeof(u32_t) - 1) / sizeof(u32_t)]
+ */
+#ifndef LWIP_DECLARE_MEMORY_ALIGNED
+#define LWIP_DECLARE_MEMORY_ALIGNED(variable_name, size) u8_t variable_name[LWIP_MEM_ALIGN_BUFFER(size)]
+#endif
+
+/** Calculate memory size for an aligned buffer - returns the next highest
+ * multiple of MEM_ALIGNMENT (e.g. LWIP_MEM_ALIGN_SIZE(3) and
+ * LWIP_MEM_ALIGN_SIZE(4) will both yield 4 for MEM_ALIGNMENT == 4).
+ */
+#ifndef LWIP_MEM_ALIGN_SIZE
+#define LWIP_MEM_ALIGN_SIZE(size) (((size) + MEM_ALIGNMENT - 1U) & ~(MEM_ALIGNMENT-1U))
+#endif
+
+/** Calculate safe memory size for an aligned buffer when using an unaligned
+ * type as storage. This includes a safety-margin on (MEM_ALIGNMENT - 1) at the
+ * start (e.g. if buffer is u8_t[] and actual data will be u32_t*)
+ */
+#ifndef LWIP_MEM_ALIGN_BUFFER
+#define LWIP_MEM_ALIGN_BUFFER(size) (((size) + MEM_ALIGNMENT - 1U))
+#endif
+
+/** Align a memory pointer to the alignment defined by MEM_ALIGNMENT
+ * so that ADDR % MEM_ALIGNMENT == 0
+ */
+#ifndef LWIP_MEM_ALIGN
+#define LWIP_MEM_ALIGN(addr) ((void *)(((mem_ptr_t)(addr) + MEM_ALIGNMENT - 1) & ~(mem_ptr_t)(MEM_ALIGNMENT-1)))
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Packed structs support.
+ * Placed BEFORE declaration of a packed struct.\n
+ * For examples of packed struct declarations, see include/lwip/prot/ subfolder.\n
+ * A port to GCC/clang is included in lwIP, if you use these compilers there is nothing to do here.
+ */
+#ifndef PACK_STRUCT_BEGIN
+#define PACK_STRUCT_BEGIN
+#endif /* PACK_STRUCT_BEGIN */
+
+/** Packed structs support.
+ * Placed AFTER declaration of a packed struct.\n
+ * For examples of packed struct declarations, see include/lwip/prot/ subfolder.\n
+ * A port to GCC/clang is included in lwIP, if you use these compilers there is nothing to do here.
+ */
+#ifndef PACK_STRUCT_END
+#define PACK_STRUCT_END
+#endif /* PACK_STRUCT_END */
+
+/** Packed structs support.
+ * Placed between end of declaration of a packed struct and trailing semicolon.\n
+ * For examples of packed struct declarations, see include/lwip/prot/ subfolder.\n
+ * A port to GCC/clang is included in lwIP, if you use these compilers there is nothing to do here.
+ */
+#ifndef PACK_STRUCT_STRUCT
+#if defined(__GNUC__) || defined(__clang__)
+#define PACK_STRUCT_STRUCT __attribute__((packed))
+#else
+#define PACK_STRUCT_STRUCT
+#endif
+#endif /* PACK_STRUCT_STRUCT */
+
+/** Packed structs support.
+ * Wraps u32_t and u16_t members.\n
+ * For examples of packed struct declarations, see include/lwip/prot/ subfolder.\n
+ * A port to GCC/clang is included in lwIP, if you use these compilers there is nothing to do here.
+ */
+#ifndef PACK_STRUCT_FIELD
+#define PACK_STRUCT_FIELD(x) x
+#endif /* PACK_STRUCT_FIELD */
+
+/** Packed structs support.
+ * Wraps u8_t members, where some compilers warn that packing is not necessary.\n
+ * For examples of packed struct declarations, see include/lwip/prot/ subfolder.\n
+ * A port to GCC/clang is included in lwIP, if you use these compilers there is nothing to do here.
+ */
+#ifndef PACK_STRUCT_FLD_8
+#define PACK_STRUCT_FLD_8(x) PACK_STRUCT_FIELD(x)
+#endif /* PACK_STRUCT_FLD_8 */
+
+/** Packed structs support.
+ * Wraps members that are packed structs themselves, where some compilers warn that packing is not necessary.\n
+ * For examples of packed struct declarations, see include/lwip/prot/ subfolder.\n
+ * A port to GCC/clang is included in lwIP, if you use these compilers there is nothing to do here.
+ */
+#ifndef PACK_STRUCT_FLD_S
+#define PACK_STRUCT_FLD_S(x) PACK_STRUCT_FIELD(x)
+#endif /* PACK_STRUCT_FLD_S */
+
+/** Packed structs support using \#include files before and after struct to be packed.\n
+ * The file included BEFORE the struct is "arch/bpstruct.h".\n
+ * The file included AFTER the struct is "arch/epstruct.h".\n
+ * This can be used to implement struct packing on MS Visual C compilers, see
+ * the Win32 port in the lwIP contrib repository for reference.
+ * For examples of packed struct declarations, see include/lwip/prot/ subfolder.\n
+ * A port to GCC/clang is included in lwIP, if you use these compilers there is nothing to do here.
+ */
+#ifdef __DOXYGEN__
+#define PACK_STRUCT_USE_INCLUDES
+#endif
+
+/** Eliminates compiler warning about unused arguments (GCC -Wextra -Wunused). */
+#ifndef LWIP_UNUSED_ARG
+#define LWIP_UNUSED_ARG(x) (void)x
+#endif /* LWIP_UNUSED_ARG */
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_ARCH_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/autoip.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/autoip.h
new file mode 100644
index 0000000..1d85bcc
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/autoip.h
@@ -0,0 +1,99 @@
+/**
+ * @file
+ *
+ * AutoIP Automatic LinkLocal IP Configuration
+ */
+
+/*
+ *
+ * Copyright (c) 2007 Dominik Spies <kontakt@dspies.de>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Dominik Spies <kontakt@dspies.de>
+ *
+ * This is a AutoIP implementation for the lwIP TCP/IP stack. It aims to conform
+ * with RFC 3927.
+ *
+ */
+
+#ifndef LWIP_HDR_AUTOIP_H
+#define LWIP_HDR_AUTOIP_H
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV4 && LWIP_AUTOIP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/netif.h"
+/* #include "lwip/udp.h" */
+#include "lwip/etharp.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** AutoIP Timing */
+#define AUTOIP_TMR_INTERVAL 100
+#define AUTOIP_TICKS_PER_SECOND (1000 / AUTOIP_TMR_INTERVAL)
+
+/** AutoIP state information per netif */
+struct autoip
+{
+ /** the currently selected, probed, announced or used LL IP-Address */
+ ip4_addr_t llipaddr;
+ /** current AutoIP state machine state */
+ u8_t state;
+ /** sent number of probes or announces, dependent on state */
+ u8_t sent_num;
+ /** ticks to wait, tick is AUTOIP_TMR_INTERVAL long */
+ u16_t ttw;
+ /** ticks until a conflict can be solved by defending */
+ u8_t lastconflict;
+ /** total number of probed/used Link Local IP-Addresses */
+ u8_t tried_llipaddr;
+};
+
+
+void autoip_set_struct(struct netif *netif, struct autoip *autoip);
+/** Remove a struct autoip previously set to the netif using autoip_set_struct() */
+#define autoip_remove_struct(netif) do { (netif)->autoip = NULL; } while (0)
+err_t autoip_start(struct netif *netif);
+err_t autoip_stop(struct netif *netif);
+void autoip_arp_reply(struct netif *netif, struct etharp_hdr *hdr);
+void autoip_tmr(void);
+void autoip_network_changed(struct netif *netif);
+u8_t autoip_supplied_address(const struct netif *netif);
+
+/* for lwIP internal use by ip4.c */
+u8_t autoip_accept_packet(struct netif *netif, const ip4_addr_t *addr);
+
+#define netif_autoip_data(netif) ((struct autoip*)netif_get_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_AUTOIP))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_IPV4 && LWIP_AUTOIP */
+
+#endif /* LWIP_HDR_AUTOIP_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/debug.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/debug.h
new file mode 100644
index 0000000..a142f1c
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/debug.h
@@ -0,0 +1,167 @@
+/**
+ * @file
+ * Debug messages infrastructure
+ */
+
+/*
+ * 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_HDR_DEBUG_H
+#define LWIP_HDR_DEBUG_H
+
+#include "lwip/arch.h"
+#include "lwip/opt.h"
+
+/**
+ * @defgroup debugging_levels LWIP_DBG_MIN_LEVEL and LWIP_DBG_TYPES_ON values
+ * @ingroup lwip_opts_debugmsg
+ * @{
+ */
+
+/** @name Debug level (LWIP_DBG_MIN_LEVEL)
+ * @{
+ */
+/** Debug level: ALL messages*/
+#define LWIP_DBG_LEVEL_ALL 0x00
+/** Debug level: Warnings. bad checksums, dropped packets, ... */
+#define LWIP_DBG_LEVEL_WARNING 0x01
+/** Debug level: Serious. memory allocation failures, ... */
+#define LWIP_DBG_LEVEL_SERIOUS 0x02
+/** Debug level: Severe */
+#define LWIP_DBG_LEVEL_SEVERE 0x03
+/**
+ * @}
+ */
+
+#define LWIP_DBG_MASK_LEVEL 0x03
+/* compatibility define only */
+#define LWIP_DBG_LEVEL_OFF LWIP_DBG_LEVEL_ALL
+
+/** @name Enable/disable debug messages completely (LWIP_DBG_TYPES_ON)
+ * @{
+ */
+/** flag for LWIP_DEBUGF to enable that debug message */
+#define LWIP_DBG_ON 0x80U
+/** flag for LWIP_DEBUGF to disable that debug message */
+#define LWIP_DBG_OFF 0x00U
+/**
+ * @}
+ */
+
+/** @name Debug message types (LWIP_DBG_TYPES_ON)
+ * @{
+ */
+/** flag for LWIP_DEBUGF indicating a tracing message (to follow program flow) */
+#define LWIP_DBG_TRACE 0x40U
+/** flag for LWIP_DEBUGF indicating a state debug message (to follow module states) */
+#define LWIP_DBG_STATE 0x20U
+/** flag for LWIP_DEBUGF indicating newly added code, not thoroughly tested yet */
+#define LWIP_DBG_FRESH 0x10U
+/** flag for LWIP_DEBUGF to halt after printing this debug message */
+#define LWIP_DBG_HALT 0x08U
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+/**
+ * @defgroup lwip_assertions Assertion handling
+ * @ingroup lwip_opts_debug
+ * @{
+ */
+/**
+ * LWIP_NOASSERT: Disable LWIP_ASSERT checks:
+ * To disable assertions define LWIP_NOASSERT in arch/cc.h.
+ */
+#ifdef __DOXYGEN__
+#define LWIP_NOASSERT
+#undef LWIP_NOASSERT
+#endif
+/**
+ * @}
+ */
+
+#ifndef LWIP_NOASSERT
+#define LWIP_ASSERT(message, assertion) do { if (!(assertion)) { \
+ LWIP_PLATFORM_ASSERT(message); }} while(0)
+#ifndef LWIP_PLATFORM_ASSERT
+#error "If you want to use LWIP_ASSERT, LWIP_PLATFORM_ASSERT(message) needs to be defined in your arch/cc.h"
+#endif
+#else /* LWIP_NOASSERT */
+#define LWIP_ASSERT(message, assertion)
+#endif /* LWIP_NOASSERT */
+
+#ifndef LWIP_ERROR
+#ifndef LWIP_NOASSERT
+#define LWIP_PLATFORM_ERROR(message) LWIP_PLATFORM_ASSERT(message)
+#elif defined LWIP_DEBUG
+#define LWIP_PLATFORM_ERROR(message) LWIP_PLATFORM_DIAG((message))
+#else
+#define LWIP_PLATFORM_ERROR(message)
+#endif
+
+/* if "expression" isn't true, then print "message" and execute "handler" expression */
+#define LWIP_ERROR(message, expression, handler) do { if (!(expression)) { \
+ LWIP_PLATFORM_ERROR(message); handler;}} while(0)
+#endif /* LWIP_ERROR */
+
+/** Enable debug message printing, but only if debug message type is enabled
+ * AND is of correct type AND is at least LWIP_DBG_LEVEL.
+ */
+#ifdef __DOXYGEN__
+#define LWIP_DEBUG
+#undef LWIP_DEBUG
+#endif
+
+#ifdef LWIP_DEBUG
+#ifndef LWIP_PLATFORM_DIAG
+#error "If you want to use LWIP_DEBUG, LWIP_PLATFORM_DIAG(message) needs to be defined in your arch/cc.h"
+#endif
+#define LWIP_DEBUGF(debug, message) do { \
+ if ( \
+ ((debug) & LWIP_DBG_ON) && \
+ ((debug) & LWIP_DBG_TYPES_ON) && \
+ ((s16_t)((debug) & LWIP_DBG_MASK_LEVEL) >= LWIP_DBG_MIN_LEVEL)) { \
+ LWIP_PLATFORM_DIAG(message); \
+ if ((debug) & LWIP_DBG_HALT) { \
+ while(1); \
+ } \
+ } \
+ } while(0)
+
+#else /* LWIP_DEBUG */
+#define LWIP_DEBUGF(debug, message)
+#endif /* LWIP_DEBUG */
+
+#endif /* LWIP_HDR_DEBUG_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/def.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/def.h
new file mode 100644
index 0000000..aaa64c7
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/def.h
@@ -0,0 +1,142 @@
+/**
+ * @file
+ * various utility macros
+ */
+
+/*
+ * 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_HDR_DEF_H
+#define LWIP_HDR_DEF_H
+
+/* arch.h might define NULL already */
+#include "lwip/arch.h"
+#include "lwip/opt.h"
+#if LWIP_PERF
+#include "arch/perf.h"
+#else /* LWIP_PERF */
+#define PERF_START /* null definition */
+#define PERF_STOP(x) /* null definition */
+#endif /* LWIP_PERF */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define LWIP_MAX(x , y) (((x) > (y)) ? (x) : (y))
+#define LWIP_MIN(x , y) (((x) < (y)) ? (x) : (y))
+
+/* Get the number of entries in an array ('x' must NOT be a pointer!) */
+#define LWIP_ARRAYSIZE(x) (sizeof(x)/sizeof((x)[0]))
+
+/** Create u32_t value from bytes */
+#define LWIP_MAKEU32(a,b,c,d) (((u32_t)((a) & 0xff) << 24) | \
+ ((u32_t)((b) & 0xff) << 16) | \
+ ((u32_t)((c) & 0xff) << 8) | \
+ (u32_t)((d) & 0xff))
+
+#ifndef NULL
+#ifdef __cplusplus
+#define NULL 0
+#else
+#define NULL ((void *)0)
+#endif
+#endif
+
+#if BYTE_ORDER == BIG_ENDIAN
+#define lwip_htons(x) (x)
+#define lwip_ntohs(x) (x)
+#define lwip_htonl(x) (x)
+#define lwip_ntohl(x) (x)
+#define PP_HTONS(x) (x)
+#define PP_NTOHS(x) (x)
+#define PP_HTONL(x) (x)
+#define PP_NTOHL(x) (x)
+#else /* BYTE_ORDER != BIG_ENDIAN */
+#ifndef lwip_htons
+u16_t lwip_htons(u16_t x);
+#endif
+#define lwip_ntohs(x) lwip_htons(x)
+
+#ifndef lwip_htonl
+u32_t lwip_htonl(u32_t x);
+#endif
+#define lwip_ntohl(x) lwip_htonl(x)
+
+/* Provide usual function names as macros for users, but this can be turned off */
+#ifndef LWIP_DONT_PROVIDE_BYTEORDER_FUNCTIONS
+#define htons(x) lwip_htons(x)
+#define ntohs(x) lwip_ntohs(x)
+#define htonl(x) lwip_htonl(x)
+#define ntohl(x) lwip_ntohl(x)
+#endif
+
+/* These macros should be calculated by the preprocessor and are used
+ with compile-time constants only (so that there is no little-endian
+ overhead at runtime). */
+#define PP_HTONS(x) ((((x) & 0x00ffUL) << 8) | (((x) & 0xff00UL) >> 8))
+#define PP_NTOHS(x) PP_HTONS(x)
+#define PP_HTONL(x) ((((x) & 0x000000ffUL) << 24) | \
+ (((x) & 0x0000ff00UL) << 8) | \
+ (((x) & 0x00ff0000UL) >> 8) | \
+ (((x) & 0xff000000UL) >> 24))
+#define PP_NTOHL(x) PP_HTONL(x)
+
+#endif /* BYTE_ORDER == BIG_ENDIAN */
+
+/* Functions that are not available as standard implementations.
+ * In cc.h, you can #define these to implementations available on
+ * your platform to save some code bytes if you use these functions
+ * in your application, too.
+ */
+
+#ifndef lwip_itoa
+/* This can be #defined to itoa() or snprintf(result, bufsize, "%d", number) depending on your platform */
+void lwip_itoa(char* result, size_t bufsize, int number);
+#endif
+#ifndef lwip_strnicmp
+/* This can be #defined to strnicmp() or strncasecmp() depending on your platform */
+int lwip_strnicmp(const char* str1, const char* str2, size_t len);
+#endif
+#ifndef lwip_stricmp
+/* This can be #defined to stricmp() or strcasecmp() depending on your platform */
+int lwip_stricmp(const char* str1, const char* str2);
+#endif
+#ifndef lwip_strnstr
+/* This can be #defined to strnstr() depending on your platform */
+char* lwip_strnstr(const char* buffer, const char* token, size_t n);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_DEF_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/dhcp.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/dhcp.h
new file mode 100644
index 0000000..ac1b18e
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/dhcp.h
@@ -0,0 +1,143 @@
+/**
+ * @file
+ * DHCP client API
+ */
+
+/*
+ * Copyright (c) 2001-2004 Leon Woestenberg <leon.woestenberg@gmx.net>
+ * Copyright (c) 2001-2004 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Leon Woestenberg <leon.woestenberg@gmx.net>
+ *
+ */
+#ifndef LWIP_HDR_DHCP_H
+#define LWIP_HDR_DHCP_H
+
+#include "lwip/opt.h"
+
+#if LWIP_DHCP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/netif.h"
+#include "lwip/udp.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** period (in seconds) of the application calling dhcp_coarse_tmr() */
+#define DHCP_COARSE_TIMER_SECS 60
+/** period (in milliseconds) of the application calling dhcp_coarse_tmr() */
+#define DHCP_COARSE_TIMER_MSECS (DHCP_COARSE_TIMER_SECS * 1000UL)
+/** period (in milliseconds) of the application calling dhcp_fine_tmr() */
+#define DHCP_FINE_TIMER_MSECS 500
+
+#define DHCP_BOOT_FILE_LEN 128U
+
+/* AutoIP cooperation flags (struct dhcp.autoip_coop_state) */
+typedef enum {
+ DHCP_AUTOIP_COOP_STATE_OFF = 0,
+ DHCP_AUTOIP_COOP_STATE_ON = 1
+} dhcp_autoip_coop_state_enum_t;
+
+struct dhcp
+{
+ /** transaction identifier of last sent request */
+ u32_t xid;
+ /** incoming msg */
+ struct dhcp_msg *msg_in;
+ /** track PCB allocation state */
+ u8_t pcb_allocated;
+ /** current DHCP state machine state */
+ u8_t state;
+ /** retries of current request */
+ u8_t tries;
+#if LWIP_DHCP_AUTOIP_COOP
+ u8_t autoip_coop_state;
+#endif
+ u8_t subnet_mask_given;
+
+ struct pbuf *p_out; /* pbuf of outcoming msg */
+ struct dhcp_msg *msg_out; /* outgoing msg */
+ u16_t options_out_len; /* outgoing msg options length */
+ u16_t request_timeout; /* #ticks with period DHCP_FINE_TIMER_SECS for request timeout */
+ u16_t t1_timeout; /* #ticks with period DHCP_COARSE_TIMER_SECS for renewal time */
+ u16_t t2_timeout; /* #ticks with period DHCP_COARSE_TIMER_SECS for rebind time */
+ u16_t t1_renew_time; /* #ticks with period DHCP_COARSE_TIMER_SECS until next renew try */
+ u16_t t2_rebind_time; /* #ticks with period DHCP_COARSE_TIMER_SECS until next rebind try */
+ u16_t lease_used; /* #ticks with period DHCP_COARSE_TIMER_SECS since last received DHCP ack */
+ u16_t t0_timeout; /* #ticks with period DHCP_COARSE_TIMER_SECS for lease time */
+ ip_addr_t server_ip_addr; /* dhcp server address that offered this lease (ip_addr_t because passed to UDP) */
+ ip4_addr_t offered_ip_addr;
+ ip4_addr_t offered_sn_mask;
+ ip4_addr_t offered_gw_addr;
+
+ u32_t offered_t0_lease; /* lease period (in seconds) */
+ u32_t offered_t1_renew; /* recommended renew time (usually 50% of lease period) */
+ u32_t offered_t2_rebind; /* recommended rebind time (usually 87.5 of lease period) */
+#if LWIP_DHCP_BOOTP_FILE
+ ip4_addr_t offered_si_addr;
+ char boot_file_name[DHCP_BOOT_FILE_LEN];
+#endif /* LWIP_DHCP_BOOTPFILE */
+};
+
+
+void dhcp_set_struct(struct netif *netif, struct dhcp *dhcp);
+/** Remove a struct dhcp previously set to the netif using dhcp_set_struct() */
+#define dhcp_remove_struct(netif) do { (netif)->dhcp = NULL; } while(0)
+void dhcp_cleanup(struct netif *netif);
+err_t dhcp_start(struct netif *netif);
+err_t dhcp_renew(struct netif *netif);
+err_t dhcp_release(struct netif *netif);
+void dhcp_stop(struct netif *netif);
+void dhcp_inform(struct netif *netif);
+void dhcp_network_changed(struct netif *netif);
+#if DHCP_DOES_ARP_CHECK
+void dhcp_arp_reply(struct netif *netif, const ip4_addr_t *addr);
+#endif
+u8_t dhcp_supplied_address(const struct netif *netif);
+/* to be called every minute */
+void dhcp_coarse_tmr(void);
+/* to be called every half second */
+void dhcp_fine_tmr(void);
+
+#if LWIP_DHCP_GET_NTP_SRV
+/** This function must exist, in other to add offered NTP servers to
+ * the NTP (or SNTP) engine.
+ * See LWIP_DHCP_MAX_NTP_SERVERS */
+extern void dhcp_set_ntp_servers(u8_t num_ntp_servers, const ip4_addr_t* ntp_server_addrs);
+#endif /* LWIP_DHCP_GET_NTP_SRV */
+
+#define netif_dhcp_data(netif) ((struct dhcp*)netif_get_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_DHCP */
+
+#endif /*LWIP_HDR_DHCP_H*/
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/dhcp6.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/dhcp6.h
new file mode 100644
index 0000000..455336d
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/dhcp6.h
@@ -0,0 +1,58 @@
+/**
+ * @file
+ *
+ * IPv6 address autoconfiguration as per RFC 4862.
+ */
+
+/*
+ * Copyright (c) 2010 Inico Technologies Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Ivan Delamer <delamer@inicotech.com>
+ *
+ * IPv6 address autoconfiguration as per RFC 4862.
+ *
+ * Please coordinate changes and requests with Ivan Delamer
+ * <delamer@inicotech.com>
+ */
+
+#ifndef LWIP_HDR_IP6_DHCP6_H
+#define LWIP_HDR_IP6_DHCP6_H
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV6_DHCP6 /* don't build if not configured for use in lwipopts.h */
+
+
+struct dhcp6
+{
+ /*@todo: implement DHCP6*/
+};
+
+#endif /* LWIP_IPV6_DHCP6 */
+
+#endif /* LWIP_HDR_IP6_DHCP6_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/dns.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/dns.h
new file mode 100644
index 0000000..1453d72
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/dns.h
@@ -0,0 +1,130 @@
+/**
+ * @file
+ * DNS API
+ */
+
+/**
+ * lwip DNS resolver header file.
+
+ * Author: Jim Pettinato
+ * April 2007
+
+ * ported from uIP resolv.c Copyright (c) 2002-2003, Adam Dunkels.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef LWIP_HDR_DNS_H
+#define LWIP_HDR_DNS_H
+
+#include "lwip/opt.h"
+
+#if LWIP_DNS
+
+#include "lwip/ip_addr.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** DNS timer period */
+#define DNS_TMR_INTERVAL 1000
+
+/* DNS resolve types: */
+#define LWIP_DNS_ADDRTYPE_IPV4 0
+#define LWIP_DNS_ADDRTYPE_IPV6 1
+#define LWIP_DNS_ADDRTYPE_IPV4_IPV6 2 /* try to resolve IPv4 first, try IPv6 if IPv4 fails only */
+#define LWIP_DNS_ADDRTYPE_IPV6_IPV4 3 /* try to resolve IPv6 first, try IPv4 if IPv6 fails only */
+#if LWIP_IPV4 && LWIP_IPV6
+#ifndef LWIP_DNS_ADDRTYPE_DEFAULT
+#define LWIP_DNS_ADDRTYPE_DEFAULT LWIP_DNS_ADDRTYPE_IPV4_IPV6
+#endif
+#elif LWIP_IPV4
+#define LWIP_DNS_ADDRTYPE_DEFAULT LWIP_DNS_ADDRTYPE_IPV4
+#else
+#define LWIP_DNS_ADDRTYPE_DEFAULT LWIP_DNS_ADDRTYPE_IPV6
+#endif
+
+#if DNS_LOCAL_HOSTLIST
+/** struct used for local host-list */
+struct local_hostlist_entry {
+ /** static hostname */
+ const char *name;
+ /** static host address in network byteorder */
+ ip_addr_t addr;
+ struct local_hostlist_entry *next;
+};
+#define DNS_LOCAL_HOSTLIST_ELEM(name, addr_init) {name, addr_init, NULL}
+#if DNS_LOCAL_HOSTLIST_IS_DYNAMIC
+#ifndef DNS_LOCAL_HOSTLIST_MAX_NAMELEN
+#define DNS_LOCAL_HOSTLIST_MAX_NAMELEN DNS_MAX_NAME_LENGTH
+#endif
+#define LOCALHOSTLIST_ELEM_SIZE ((sizeof(struct local_hostlist_entry) + DNS_LOCAL_HOSTLIST_MAX_NAMELEN + 1))
+#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
+#endif /* DNS_LOCAL_HOSTLIST */
+
+#if LWIP_IPV4
+extern const ip_addr_t dns_mquery_v4group;
+#endif /* LWIP_IPV4 */
+#if LWIP_IPV6
+extern const ip_addr_t dns_mquery_v6group;
+#endif /* LWIP_IPV6 */
+
+/** Callback which is invoked when a hostname is found.
+ * A function of this type must be implemented by the application using the DNS resolver.
+ * @param name pointer to the name that was looked up.
+ * @param ipaddr pointer to an ip_addr_t containing the IP address of the hostname,
+ * or NULL if the name could not be found (or on any other error).
+ * @param callback_arg a user-specified callback argument passed to dns_gethostbyname
+*/
+typedef void (*dns_found_callback)(const char *name, const ip_addr_t *ipaddr, void *callback_arg);
+
+void dns_init(void);
+void dns_tmr(void);
+void dns_setserver(u8_t numdns, const ip_addr_t *dnsserver);
+const ip_addr_t* dns_getserver(u8_t numdns);
+err_t dns_gethostbyname(const char *hostname, ip_addr_t *addr,
+ dns_found_callback found, void *callback_arg);
+err_t dns_gethostbyname_addrtype(const char *hostname, ip_addr_t *addr,
+ dns_found_callback found, void *callback_arg,
+ u8_t dns_addrtype);
+
+
+#if DNS_LOCAL_HOSTLIST
+size_t dns_local_iterate(dns_found_callback iterator_fn, void *iterator_arg);
+err_t dns_local_lookup(const char *hostname, ip_addr_t *addr, u8_t dns_addrtype);
+#if DNS_LOCAL_HOSTLIST_IS_DYNAMIC
+int dns_local_removehost(const char *hostname, const ip_addr_t *addr);
+err_t dns_local_addhost(const char *hostname, const ip_addr_t *addr);
+#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
+#endif /* DNS_LOCAL_HOSTLIST */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_DNS */
+
+#endif /* LWIP_HDR_DNS_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/err.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/err.h
new file mode 100644
index 0000000..84e528d
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/err.h
@@ -0,0 +1,119 @@
+/**
+ * @file
+ * lwIP Error codes
+ */
+/*
+ * 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_HDR_ERR_H
+#define LWIP_HDR_ERR_H
+
+#include "lwip/opt.h"
+#include "lwip/arch.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @defgroup infrastructure_errors Error codes
+ * @ingroup infrastructure
+ * @{
+ */
+
+/** Define LWIP_ERR_T in cc.h if you want to use
+ * a different type for your platform (must be signed). */
+#ifdef LWIP_ERR_T
+typedef LWIP_ERR_T err_t;
+#else /* LWIP_ERR_T */
+typedef s8_t err_t;
+#endif /* LWIP_ERR_T*/
+
+/** Definitions for error constants. */
+typedef enum {
+/** No error, everything OK. */
+ ERR_OK = 0,
+/** Out of memory error. */
+ ERR_MEM = -1,
+/** Buffer error. */
+ ERR_BUF = -2,
+/** Timeout. */
+ ERR_TIMEOUT = -3,
+/** Routing problem. */
+ ERR_RTE = -4,
+/** Operation in progress */
+ ERR_INPROGRESS = -5,
+/** Illegal value. */
+ ERR_VAL = -6,
+/** Operation would block. */
+ ERR_WOULDBLOCK = -7,
+/** Address in use. */
+ ERR_USE = -8,
+/** Already connecting. */
+ ERR_ALREADY = -9,
+/** Conn already established.*/
+ ERR_ISCONN = -10,
+/** Not connected. */
+ ERR_CONN = -11,
+/** Low-level netif error */
+ ERR_IF = -12,
+
+/** Connection aborted. */
+ ERR_ABRT = -13,
+/** Connection reset. */
+ ERR_RST = -14,
+/** Connection closed. */
+ ERR_CLSD = -15,
+/** Illegal argument. */
+ ERR_ARG = -16
+} err_enum_t;
+
+#define ERR_IS_FATAL(e) ((e) <= ERR_ABRT)
+
+/**
+ * @}
+ */
+
+#ifdef LWIP_DEBUG
+extern const char *lwip_strerr(err_t err);
+#else
+#define lwip_strerr(x) ""
+#endif /* LWIP_DEBUG */
+
+#if !NO_SYS
+int err_to_errno(err_t err);
+#endif /* !NO_SYS */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_ERR_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/errno.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/errno.h
new file mode 100644
index 0000000..641cffb
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/errno.h
@@ -0,0 +1,193 @@
+/**
+ * @file
+ * Posix Errno defines
+ */
+
+/*
+ * 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_HDR_ERRNO_H
+#define LWIP_HDR_ERRNO_H
+
+#include "lwip/opt.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef LWIP_PROVIDE_ERRNO
+
+#define EPERM 1 /* Operation not permitted */
+#define ENOENT 2 /* No such file or directory */
+#define ESRCH 3 /* No such process */
+#define EINTR 4 /* Interrupted system call */
+#define EIO 5 /* I/O error */
+#define ENXIO 6 /* No such device or address */
+#define E2BIG 7 /* Arg list too long */
+#define ENOEXEC 8 /* Exec format error */
+#define EBADF 9 /* Bad file number */
+#define ECHILD 10 /* No child processes */
+#define EAGAIN 11 /* Try again */
+#define ENOMEM 12 /* Out of memory */
+#define EACCES 13 /* Permission denied */
+#define EFAULT 14 /* Bad address */
+#define ENOTBLK 15 /* Block device required */
+#define EBUSY 16 /* Device or resource busy */
+#define EEXIST 17 /* File exists */
+#define EXDEV 18 /* Cross-device link */
+#define ENODEV 19 /* No such device */
+#define ENOTDIR 20 /* Not a directory */
+#define EISDIR 21 /* Is a directory */
+#define EINVAL 22 /* Invalid argument */
+#define ENFILE 23 /* File table overflow */
+#define EMFILE 24 /* Too many open files */
+#define ENOTTY 25 /* Not a typewriter */
+#define ETXTBSY 26 /* Text file busy */
+#define EFBIG 27 /* File too large */
+#define ENOSPC 28 /* No space left on device */
+#define ESPIPE 29 /* Illegal seek */
+#define EROFS 30 /* Read-only file system */
+#define EMLINK 31 /* Too many links */
+#define EPIPE 32 /* Broken pipe */
+#define EDOM 33 /* Math argument out of domain of func */
+#define ERANGE 34 /* Math result not representable */
+#define EDEADLK 35 /* Resource deadlock would occur */
+#define ENAMETOOLONG 36 /* File name too long */
+#define ENOLCK 37 /* No record locks available */
+#define ENOSYS 38 /* Function not implemented */
+#define ENOTEMPTY 39 /* Directory not empty */
+#define ELOOP 40 /* Too many symbolic links encountered */
+#define EWOULDBLOCK EAGAIN /* Operation would block */
+#define ENOMSG 42 /* No message of desired type */
+#define EIDRM 43 /* Identifier removed */
+#define ECHRNG 44 /* Channel number out of range */
+#define EL2NSYNC 45 /* Level 2 not synchronized */
+#define EL3HLT 46 /* Level 3 halted */
+#define EL3RST 47 /* Level 3 reset */
+#define ELNRNG 48 /* Link number out of range */
+#define EUNATCH 49 /* Protocol driver not attached */
+#define ENOCSI 50 /* No CSI structure available */
+#define EL2HLT 51 /* Level 2 halted */
+#define EBADE 52 /* Invalid exchange */
+#define EBADR 53 /* Invalid request descriptor */
+#define EXFULL 54 /* Exchange full */
+#define ENOANO 55 /* No anode */
+#define EBADRQC 56 /* Invalid request code */
+#define EBADSLT 57 /* Invalid slot */
+
+#define EDEADLOCK EDEADLK
+
+#define EBFONT 59 /* Bad font file format */
+#define ENOSTR 60 /* Device not a stream */
+#define ENODATA 61 /* No data available */
+#define ETIME 62 /* Timer expired */
+#define ENOSR 63 /* Out of streams resources */
+#define ENONET 64 /* Machine is not on the network */
+#define ENOPKG 65 /* Package not installed */
+#define EREMOTE 66 /* Object is remote */
+#define ENOLINK 67 /* Link has been severed */
+#define EADV 68 /* Advertise error */
+#define ESRMNT 69 /* Srmount error */
+#define ECOMM 70 /* Communication error on send */
+#define EPROTO 71 /* Protocol error */
+#define EMULTIHOP 72 /* Multihop attempted */
+#define EDOTDOT 73 /* RFS specific error */
+#define EBADMSG 74 /* Not a data message */
+#define EOVERFLOW 75 /* Value too large for defined data type */
+#define ENOTUNIQ 76 /* Name not unique on network */
+#define EBADFD 77 /* File descriptor in bad state */
+#define EREMCHG 78 /* Remote address changed */
+#define ELIBACC 79 /* Can not access a needed shared library */
+#define ELIBBAD 80 /* Accessing a corrupted shared library */
+#define ELIBSCN 81 /* .lib section in a.out corrupted */
+#define ELIBMAX 82 /* Attempting to link in too many shared libraries */
+#define ELIBEXEC 83 /* Cannot exec a shared library directly */
+#define EILSEQ 84 /* Illegal byte sequence */
+#define ERESTART 85 /* Interrupted system call should be restarted */
+#define ESTRPIPE 86 /* Streams pipe error */
+#define EUSERS 87 /* Too many users */
+#define ENOTSOCK 88 /* Socket operation on non-socket */
+#define EDESTADDRREQ 89 /* Destination address required */
+#define EMSGSIZE 90 /* Message too long */
+#define EPROTOTYPE 91 /* Protocol wrong type for socket */
+#define ENOPROTOOPT 92 /* Protocol not available */
+#define EPROTONOSUPPORT 93 /* Protocol not supported */
+#define ESOCKTNOSUPPORT 94 /* Socket type not supported */
+#define EOPNOTSUPP 95 /* Operation not supported on transport endpoint */
+#define EPFNOSUPPORT 96 /* Protocol family not supported */
+#define EAFNOSUPPORT 97 /* Address family not supported by protocol */
+#define EADDRINUSE 98 /* Address already in use */
+#define EADDRNOTAVAIL 99 /* Cannot assign requested address */
+#define ENETDOWN 100 /* Network is down */
+#define ENETUNREACH 101 /* Network is unreachable */
+#define ENETRESET 102 /* Network dropped connection because of reset */
+#define ECONNABORTED 103 /* Software caused connection abort */
+#define ECONNRESET 104 /* Connection reset by peer */
+#define ENOBUFS 105 /* No buffer space available */
+#define EISCONN 106 /* Transport endpoint is already connected */
+#define ENOTCONN 107 /* Transport endpoint is not connected */
+#define ESHUTDOWN 108 /* Cannot send after transport endpoint shutdown */
+#define ETOOMANYREFS 109 /* Too many references: cannot splice */
+#define ETIMEDOUT 110 /* Connection timed out */
+#define ECONNREFUSED 111 /* Connection refused */
+#define EHOSTDOWN 112 /* Host is down */
+#define EHOSTUNREACH 113 /* No route to host */
+#define EALREADY 114 /* Operation already in progress */
+#define EINPROGRESS 115 /* Operation now in progress */
+#define ESTALE 116 /* Stale NFS file handle */
+#define EUCLEAN 117 /* Structure needs cleaning */
+#define ENOTNAM 118 /* Not a XENIX named type file */
+#define ENAVAIL 119 /* No XENIX semaphores available */
+#define EISNAM 120 /* Is a named type file */
+#define EREMOTEIO 121 /* Remote I/O error */
+#define EDQUOT 122 /* Quota exceeded */
+
+#define ENOMEDIUM 123 /* No medium found */
+#define EMEDIUMTYPE 124 /* Wrong medium type */
+
+#ifndef errno
+extern int errno;
+#endif
+
+#else /* LWIP_PROVIDE_ERRNO */
+
+/* Define LWIP_ERRNO_INCLUDE to <errno.h> to include the error defines here */
+#ifdef LWIP_ERRNO_INCLUDE
+#include LWIP_ERRNO_INCLUDE
+#endif /* LWIP_ERRNO_INCLUDE */
+
+#endif /* LWIP_PROVIDE_ERRNO */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_ERRNO_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/etharp.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/etharp.h
new file mode 100644
index 0000000..7080a19
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/etharp.h
@@ -0,0 +1,106 @@
+/**
+ * @file
+ * Ethernet output function - handles OUTGOING ethernet level traffic, implements
+ * ARP resolving.
+ * To be used in most low-level netif implementations
+ */
+
+/*
+ * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
+ * Copyright (c) 2003-2004 Leon Woestenberg <leon.woestenberg@axon.tv>
+ * Copyright (c) 2003-2004 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#ifndef LWIP_HDR_NETIF_ETHARP_H
+#define LWIP_HDR_NETIF_ETHARP_H
+
+#include "lwip/opt.h"
+
+#if LWIP_ARP || LWIP_ETHERNET /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/pbuf.h"
+#include "lwip/ip4_addr.h"
+#include "lwip/netif.h"
+#include "lwip/ip4.h"
+#include "lwip/prot/ethernet.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if LWIP_IPV4 && LWIP_ARP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/prot/etharp.h"
+
+/** 1 seconds period */
+#define ARP_TMR_INTERVAL 1000
+
+#if ARP_QUEUEING
+/** struct for queueing outgoing packets for unknown address
+ * defined here to be accessed by memp.h
+ */
+struct etharp_q_entry {
+ struct etharp_q_entry *next;
+ struct pbuf *p;
+};
+#endif /* ARP_QUEUEING */
+
+#define etharp_init() /* Compatibility define, no init needed. */
+void etharp_tmr(void);
+s8_t etharp_find_addr(struct netif *netif, const ip4_addr_t *ipaddr,
+ struct eth_addr **eth_ret, const ip4_addr_t **ip_ret);
+u8_t etharp_get_entry(u8_t i, ip4_addr_t **ipaddr, struct netif **netif, struct eth_addr **eth_ret);
+err_t etharp_output(struct netif *netif, struct pbuf *q, const ip4_addr_t *ipaddr);
+err_t etharp_query(struct netif *netif, const ip4_addr_t *ipaddr, struct pbuf *q);
+err_t etharp_request(struct netif *netif, const ip4_addr_t *ipaddr);
+/** For Ethernet network interfaces, we might want to send "gratuitous ARP";
+ * this is an ARP packet sent by a node in order to spontaneously cause other
+ * nodes to update an entry in their ARP cache.
+ * From RFC 3220 "IP Mobility Support for IPv4" section 4.6. */
+#define etharp_gratuitous(netif) etharp_request((netif), netif_ip4_addr(netif))
+void etharp_cleanup_netif(struct netif *netif);
+
+#if ETHARP_SUPPORT_STATIC_ENTRIES
+err_t etharp_add_static_entry(const ip4_addr_t *ipaddr, struct eth_addr *ethaddr);
+err_t etharp_remove_static_entry(const ip4_addr_t *ipaddr);
+#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
+
+#endif /* LWIP_IPV4 && LWIP_ARP */
+
+void etharp_input(struct pbuf *p, struct netif *netif);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_ARP || LWIP_ETHERNET */
+
+#endif /* LWIP_HDR_NETIF_ETHARP_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/ethip6.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/ethip6.h
new file mode 100644
index 0000000..5e88dff
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/ethip6.h
@@ -0,0 +1,68 @@
+/**
+ * @file
+ *
+ * Ethernet output for IPv6. Uses ND tables for link-layer addressing.
+ */
+
+/*
+ * Copyright (c) 2010 Inico Technologies Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Ivan Delamer <delamer@inicotech.com>
+ *
+ *
+ * Please coordinate changes and requests with Ivan Delamer
+ * <delamer@inicotech.com>
+ */
+
+#ifndef LWIP_HDR_ETHIP6_H
+#define LWIP_HDR_ETHIP6_H
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV6 && LWIP_ETHERNET /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/pbuf.h"
+#include "lwip/ip6.h"
+#include "lwip/ip6_addr.h"
+#include "lwip/netif.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+err_t ethip6_output(struct netif *netif, struct pbuf *q, const ip6_addr_t *ip6addr);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_IPV6 && LWIP_ETHERNET */
+
+#endif /* LWIP_HDR_ETHIP6_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/icmp.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/icmp.h
new file mode 100644
index 0000000..f5a31fd
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/icmp.h
@@ -0,0 +1,110 @@
+/**
+ * @file
+ * ICMP API
+ */
+
+/*
+ * 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_HDR_ICMP_H
+#define LWIP_HDR_ICMP_H
+
+#include "lwip/opt.h"
+#include "lwip/pbuf.h"
+#include "lwip/ip_addr.h"
+#include "lwip/netif.h"
+#include "lwip/prot/icmp.h"
+
+#if LWIP_IPV6 && LWIP_ICMP6
+#include "lwip/icmp6.h"
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** ICMP destination unreachable codes */
+enum icmp_dur_type {
+ /** net unreachable */
+ ICMP_DUR_NET = 0,
+ /** host unreachable */
+ ICMP_DUR_HOST = 1,
+ /** protocol unreachable */
+ ICMP_DUR_PROTO = 2,
+ /** port unreachable */
+ ICMP_DUR_PORT = 3,
+ /** fragmentation needed and DF set */
+ ICMP_DUR_FRAG = 4,
+ /** source route failed */
+ ICMP_DUR_SR = 5
+};
+
+/** ICMP time exceeded codes */
+enum icmp_te_type {
+ /** time to live exceeded in transit */
+ ICMP_TE_TTL = 0,
+ /** fragment reassembly time exceeded */
+ ICMP_TE_FRAG = 1
+};
+
+#if LWIP_IPV4 && LWIP_ICMP /* don't build if not configured for use in lwipopts.h */
+
+void icmp_input(struct pbuf *p, struct netif *inp);
+void icmp_dest_unreach(struct pbuf *p, enum icmp_dur_type t);
+void icmp_time_exceeded(struct pbuf *p, enum icmp_te_type t);
+
+#endif /* LWIP_IPV4 && LWIP_ICMP */
+
+#if LWIP_IPV4 && LWIP_IPV6
+#if LWIP_ICMP && LWIP_ICMP6
+#define icmp_port_unreach(isipv6, pbuf) ((isipv6) ? \
+ icmp6_dest_unreach(pbuf, ICMP6_DUR_PORT) : \
+ icmp_dest_unreach(pbuf, ICMP_DUR_PORT))
+#elif LWIP_ICMP
+#define icmp_port_unreach(isipv6, pbuf) do{ if(!(isipv6)) { icmp_dest_unreach(pbuf, ICMP_DUR_PORT);}}while(0)
+#elif LWIP_ICMP6
+#define icmp_port_unreach(isipv6, pbuf) do{ if(isipv6) { icmp6_dest_unreach(pbuf, ICMP6_DUR_PORT);}}while(0)
+#else
+#define icmp_port_unreach(isipv6, pbuf)
+#endif
+#elif LWIP_IPV6 && LWIP_ICMP6
+#define icmp_port_unreach(isipv6, pbuf) icmp6_dest_unreach(pbuf, ICMP6_DUR_PORT)
+#elif LWIP_IPV4 && LWIP_ICMP
+#define icmp_port_unreach(isipv6, pbuf) icmp_dest_unreach(pbuf, ICMP_DUR_PORT)
+#else /* (LWIP_IPV6 && LWIP_ICMP6) || (LWIP_IPV4 && LWIP_ICMP) */
+#define icmp_port_unreach(isipv6, pbuf)
+#endif /* (LWIP_IPV6 && LWIP_ICMP6) || (LWIP_IPV4 && LWIP_ICMP) LWIP_IPV4*/
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_ICMP_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/icmp6.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/icmp6.h
new file mode 100644
index 0000000..a29dc8c
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/icmp6.h
@@ -0,0 +1,70 @@
+/**
+ * @file
+ *
+ * IPv6 version of ICMP, as per RFC 4443.
+ */
+
+/*
+ * Copyright (c) 2010 Inico Technologies Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Ivan Delamer <delamer@inicotech.com>
+ *
+ *
+ * Please coordinate changes and requests with Ivan Delamer
+ * <delamer@inicotech.com>
+ */
+#ifndef LWIP_HDR_ICMP6_H
+#define LWIP_HDR_ICMP6_H
+
+#include "lwip/opt.h"
+#include "lwip/pbuf.h"
+#include "lwip/ip6_addr.h"
+#include "lwip/netif.h"
+#include "lwip/prot/icmp6.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if LWIP_ICMP6 && LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */
+
+void icmp6_input(struct pbuf *p, struct netif *inp);
+void icmp6_dest_unreach(struct pbuf *p, enum icmp6_dur_code c);
+void icmp6_packet_too_big(struct pbuf *p, u32_t mtu);
+void icmp6_time_exceeded(struct pbuf *p, enum icmp6_te_code c);
+void icmp6_param_problem(struct pbuf *p, enum icmp6_pp_code c, u32_t pointer);
+
+#endif /* LWIP_ICMP6 && LWIP_IPV6 */
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* LWIP_HDR_ICMP6_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/igmp.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/igmp.h
new file mode 100644
index 0000000..ffd80e6
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/igmp.h
@@ -0,0 +1,115 @@
+/**
+ * @file
+ * IGMP API
+ */
+
+/*
+ * Copyright (c) 2002 CITEL Technologies Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of CITEL Technologies Ltd nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY CITEL TECHNOLOGIES AND CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL CITEL TECHNOLOGIES OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This file is a contribution to the lwIP TCP/IP stack.
+ * The Swedish Institute of Computer Science and Adam Dunkels
+ * are specifically granted permission to redistribute this
+ * source code.
+*/
+
+#ifndef LWIP_HDR_IGMP_H
+#define LWIP_HDR_IGMP_H
+
+#include "lwip/opt.h"
+#include "lwip/ip_addr.h"
+#include "lwip/netif.h"
+#include "lwip/pbuf.h"
+
+#if LWIP_IPV4 && LWIP_IGMP /* don't build if not configured for use in lwipopts.h */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* IGMP timer */
+#define IGMP_TMR_INTERVAL 100 /* Milliseconds */
+#define IGMP_V1_DELAYING_MEMBER_TMR (1000/IGMP_TMR_INTERVAL)
+#define IGMP_JOIN_DELAYING_MEMBER_TMR (500 /IGMP_TMR_INTERVAL)
+
+/* Compatibility defines (don't use for new code) */
+#define IGMP_DEL_MAC_FILTER NETIF_DEL_MAC_FILTER
+#define IGMP_ADD_MAC_FILTER NETIF_ADD_MAC_FILTER
+
+/**
+ * igmp group structure - there is
+ * a list of groups for each interface
+ * these should really be linked from the interface, but
+ * if we keep them separate we will not affect the lwip original code
+ * too much
+ *
+ * There will be a group for the all systems group address but this
+ * will not run the state machine as it is used to kick off reports
+ * from all the other groups
+ */
+struct igmp_group {
+ /** next link */
+ struct igmp_group *next;
+ /** multicast address */
+ ip4_addr_t group_address;
+ /** signifies we were the last person to report */
+ u8_t last_reporter_flag;
+ /** current state of the group */
+ u8_t group_state;
+ /** timer for reporting, negative is OFF */
+ u16_t timer;
+ /** counter of simultaneous uses */
+ u8_t use;
+};
+
+/* Prototypes */
+void igmp_init(void);
+err_t igmp_start(struct netif *netif);
+err_t igmp_stop(struct netif *netif);
+void igmp_report_groups(struct netif *netif);
+struct igmp_group *igmp_lookfor_group(struct netif *ifp, const ip4_addr_t *addr);
+void igmp_input(struct pbuf *p, struct netif *inp, const ip4_addr_t *dest);
+err_t igmp_joingroup(const ip4_addr_t *ifaddr, const ip4_addr_t *groupaddr);
+err_t igmp_joingroup_netif(struct netif *netif, const ip4_addr_t *groupaddr);
+err_t igmp_leavegroup(const ip4_addr_t *ifaddr, const ip4_addr_t *groupaddr);
+err_t igmp_leavegroup_netif(struct netif *netif, const ip4_addr_t *groupaddr);
+void igmp_tmr(void);
+
+/** @ingroup igmp
+ * Get list head of IGMP groups for netif.
+ * Note: The allsystems group IP is contained in the list as first entry.
+ * @see @ref netif_set_igmp_mac_filter()
+ */
+#define netif_igmp_data(netif) ((struct igmp_group *)netif_get_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_IGMP))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_IPV4 && LWIP_IGMP */
+
+#endif /* LWIP_HDR_IGMP_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/inet.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/inet.h
new file mode 100644
index 0000000..4a34f02
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/inet.h
@@ -0,0 +1,172 @@
+/**
+ * @file
+ * This file (together with sockets.h) aims to provide structs and functions from
+ * - arpa/inet.h
+ * - netinet/in.h
+ *
+ */
+
+/*
+ * 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_HDR_INET_H
+#define LWIP_HDR_INET_H
+
+#include "lwip/opt.h"
+#include "lwip/def.h"
+#include "lwip/ip_addr.h"
+#include "lwip/ip6_addr.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* If your port already typedef's in_addr_t, define IN_ADDR_T_DEFINED
+ to prevent this code from redefining it. */
+#if !defined(in_addr_t) && !defined(IN_ADDR_T_DEFINED)
+typedef u32_t in_addr_t;
+#endif
+
+struct in_addr {
+ in_addr_t s_addr;
+};
+
+struct in6_addr {
+ union {
+ u32_t u32_addr[4];
+ u8_t u8_addr[16];
+ } un;
+#define s6_addr un.u8_addr
+};
+
+/** 255.255.255.255 */
+#define INADDR_NONE IPADDR_NONE
+/** 127.0.0.1 */
+#define INADDR_LOOPBACK IPADDR_LOOPBACK
+/** 0.0.0.0 */
+#define INADDR_ANY IPADDR_ANY
+/** 255.255.255.255 */
+#define INADDR_BROADCAST IPADDR_BROADCAST
+
+/** This macro can be used to initialize a variable of type struct in6_addr
+ to the IPv6 wildcard address. */
+#define IN6ADDR_ANY_INIT {{{0,0,0,0}}}
+/** This macro can be used to initialize a variable of type struct in6_addr
+ to the IPv6 loopback address. */
+#define IN6ADDR_LOOPBACK_INIT {{{0,0,0,PP_HTONL(1)}}}
+/** This variable is initialized by the system to contain the wildcard IPv6 address. */
+extern const struct in6_addr in6addr_any;
+
+/* Definitions of the bits in an (IPv4) Internet address integer.
+
+ On subnets, host and network parts are found according to
+ the subnet mask, not these masks. */
+#define IN_CLASSA(a) IP_CLASSA(a)
+#define IN_CLASSA_NET IP_CLASSA_NET
+#define IN_CLASSA_NSHIFT IP_CLASSA_NSHIFT
+#define IN_CLASSA_HOST IP_CLASSA_HOST
+#define IN_CLASSA_MAX IP_CLASSA_MAX
+
+#define IN_CLASSB(b) IP_CLASSB(b)
+#define IN_CLASSB_NET IP_CLASSB_NET
+#define IN_CLASSB_NSHIFT IP_CLASSB_NSHIFT
+#define IN_CLASSB_HOST IP_CLASSB_HOST
+#define IN_CLASSB_MAX IP_CLASSB_MAX
+
+#define IN_CLASSC(c) IP_CLASSC(c)
+#define IN_CLASSC_NET IP_CLASSC_NET
+#define IN_CLASSC_NSHIFT IP_CLASSC_NSHIFT
+#define IN_CLASSC_HOST IP_CLASSC_HOST
+#define IN_CLASSC_MAX IP_CLASSC_MAX
+
+#define IN_CLASSD(d) IP_CLASSD(d)
+#define IN_CLASSD_NET IP_CLASSD_NET /* These ones aren't really */
+#define IN_CLASSD_NSHIFT IP_CLASSD_NSHIFT /* net and host fields, but */
+#define IN_CLASSD_HOST IP_CLASSD_HOST /* routing needn't know. */
+#define IN_CLASSD_MAX IP_CLASSD_MAX
+
+#define IN_MULTICAST(a) IP_MULTICAST(a)
+
+#define IN_EXPERIMENTAL(a) IP_EXPERIMENTAL(a)
+#define IN_BADCLASS(a) IP_BADCLASS(a)
+
+#define IN_LOOPBACKNET IP_LOOPBACKNET
+
+
+#ifndef INET_ADDRSTRLEN
+#define INET_ADDRSTRLEN IP4ADDR_STRLEN_MAX
+#endif
+#if LWIP_IPV6
+#ifndef INET6_ADDRSTRLEN
+#define INET6_ADDRSTRLEN IP6ADDR_STRLEN_MAX
+#endif
+#endif
+
+#if LWIP_IPV4
+
+#define inet_addr_from_ip4addr(target_inaddr, source_ipaddr) ((target_inaddr)->s_addr = ip4_addr_get_u32(source_ipaddr))
+#define inet_addr_to_ip4addr(target_ipaddr, source_inaddr) (ip4_addr_set_u32(target_ipaddr, (source_inaddr)->s_addr))
+/* ATTENTION: the next define only works because both s_addr and ip4_addr_t are an u32_t effectively! */
+#define inet_addr_to_ip4addr_p(target_ip4addr_p, source_inaddr) ((target_ip4addr_p) = (ip4_addr_t*)&((source_inaddr)->s_addr))
+
+/* directly map this to the lwip internal functions */
+#define inet_addr(cp) ipaddr_addr(cp)
+#define inet_aton(cp, addr) ip4addr_aton(cp, (ip4_addr_t*)addr)
+#define inet_ntoa(addr) ip4addr_ntoa((const ip4_addr_t*)&(addr))
+#define inet_ntoa_r(addr, buf, buflen) ip4addr_ntoa_r((const ip4_addr_t*)&(addr), buf, buflen)
+
+#endif /* LWIP_IPV4 */
+
+#if LWIP_IPV6
+#define inet6_addr_from_ip6addr(target_in6addr, source_ip6addr) {(target_in6addr)->un.u32_addr[0] = (source_ip6addr)->addr[0]; \
+ (target_in6addr)->un.u32_addr[1] = (source_ip6addr)->addr[1]; \
+ (target_in6addr)->un.u32_addr[2] = (source_ip6addr)->addr[2]; \
+ (target_in6addr)->un.u32_addr[3] = (source_ip6addr)->addr[3];}
+#define inet6_addr_to_ip6addr(target_ip6addr, source_in6addr) {(target_ip6addr)->addr[0] = (source_in6addr)->un.u32_addr[0]; \
+ (target_ip6addr)->addr[1] = (source_in6addr)->un.u32_addr[1]; \
+ (target_ip6addr)->addr[2] = (source_in6addr)->un.u32_addr[2]; \
+ (target_ip6addr)->addr[3] = (source_in6addr)->un.u32_addr[3];}
+/* ATTENTION: the next define only works because both in6_addr and ip6_addr_t are an u32_t[4] effectively! */
+#define inet6_addr_to_ip6addr_p(target_ip6addr_p, source_in6addr) ((target_ip6addr_p) = (ip6_addr_t*)(source_in6addr))
+
+/* directly map this to the lwip internal functions */
+#define inet6_aton(cp, addr) ip6addr_aton(cp, (ip6_addr_t*)addr)
+#define inet6_ntoa(addr) ip6addr_ntoa((const ip6_addr_t*)&(addr))
+#define inet6_ntoa_r(addr, buf, buflen) ip6addr_ntoa_r((const ip6_addr_t*)&(addr), buf, buflen)
+
+#endif /* LWIP_IPV6 */
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_INET_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/inet6.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/inet6.h
new file mode 100644
index 0000000..afaa923
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/inet6.h
@@ -0,0 +1,92 @@
+/**
+ * @file
+ *
+ * INET v6 addresses.
+ */
+
+/*
+ * Copyright (c) 2010 Inico Technologies Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Ivan Delamer <delamer@inicotech.com>
+ *
+ *
+ * Please coordinate changes and requests with Ivan Delamer
+ * <delamer@inicotech.com>
+ */
+#ifndef LWIP_HDR_INET6_H
+#define LWIP_HDR_INET6_H
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV6 && LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/ip6_addr.h"
+#include "lwip/def.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** For compatibility with BSD code */
+struct in6_addr {
+ union {
+ u8_t u8_addr[16];
+ u32_t u32_addr[4];
+ } un;
+#define s6_addr un.u8_addr
+};
+
+#define IN6ADDR_ANY_INIT {0,0,0,0}
+#define IN6ADDR_LOOPBACK_INIT {0,0,0,PP_HTONL(1)}
+
+
+#define inet6_addr_from_ip6addr(target_in6addr, source_ip6addr) {(target_in6addr)->un.u32_addr[0] = (source_ip6addr)->addr[0]; \
+ (target_in6addr)->un.u32_addr[1] = (source_ip6addr)->addr[1]; \
+ (target_in6addr)->un.u32_addr[2] = (source_ip6addr)->addr[2]; \
+ (target_in6addr)->un.u32_addr[3] = (source_ip6addr)->addr[3];}
+#define inet6_addr_to_ip6addr(target_ip6addr, source_in6addr) {(target_ip6addr)->addr[0] = (source_in6addr)->un.u32_addr[0]; \
+ (target_ip6addr)->addr[1] = (source_in6addr)->un.u32_addr[1]; \
+ (target_ip6addr)->addr[2] = (source_in6addr)->un.u32_addr[2]; \
+ (target_ip6addr)->addr[3] = (source_in6addr)->un.u32_addr[3];}
+/* ATTENTION: the next define only works because both in6_addr and ip6_addr_t are an u32_t[4] effectively! */
+#define inet6_addr_to_ip6addr_p(target_ip6addr_p, source_in6addr) ((target_ip6addr_p) = (ip6_addr_t*)(source_in6addr))
+
+/* directly map this to the lwip internal functions */
+#define inet6_aton(cp, addr) ip6addr_aton(cp, (ip6_addr_t*)addr)
+#define inet6_ntoa(addr) ip6addr_ntoa((ip6_addr_t*)&(addr))
+#define inet6_ntoa_r(addr, buf, buflen) ip6addr_ntoa_r((ip6_addr_t*)&(addr), buf, buflen)
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_IPV6 */
+
+#endif /* LWIP_HDR_INET6_H */
+
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/inet_chksum.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/inet_chksum.h
new file mode 100644
index 0000000..4e23d7f
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/inet_chksum.h
@@ -0,0 +1,105 @@
+/**
+ * @file
+ * IP checksum calculation 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>
+ *
+ */
+#ifndef LWIP_HDR_INET_CHKSUM_H
+#define LWIP_HDR_INET_CHKSUM_H
+
+#include "lwip/opt.h"
+
+#include "lwip/pbuf.h"
+#include "lwip/ip_addr.h"
+
+/** Swap the bytes in an u16_t: much like lwip_htons() for little-endian */
+#ifndef SWAP_BYTES_IN_WORD
+#define SWAP_BYTES_IN_WORD(w) (((w) & 0xff) << 8) | (((w) & 0xff00) >> 8)
+#endif /* SWAP_BYTES_IN_WORD */
+
+/** Split an u32_t in two u16_ts and add them up */
+#ifndef FOLD_U32T
+#define FOLD_U32T(u) (((u) >> 16) + ((u) & 0x0000ffffUL))
+#endif
+
+#if LWIP_CHECKSUM_ON_COPY
+/** Function-like macro: same as MEMCPY but returns the checksum of copied data
+ as u16_t */
+# ifndef LWIP_CHKSUM_COPY
+# define LWIP_CHKSUM_COPY(dst, src, len) lwip_chksum_copy(dst, src, len)
+# ifndef LWIP_CHKSUM_COPY_ALGORITHM
+# define LWIP_CHKSUM_COPY_ALGORITHM 1
+# endif /* LWIP_CHKSUM_COPY_ALGORITHM */
+# else /* LWIP_CHKSUM_COPY */
+# define LWIP_CHKSUM_COPY_ALGORITHM 0
+# endif /* LWIP_CHKSUM_COPY */
+#else /* LWIP_CHECKSUM_ON_COPY */
+# define LWIP_CHKSUM_COPY_ALGORITHM 0
+#endif /* LWIP_CHECKSUM_ON_COPY */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+u16_t inet_chksum(const void *dataptr, u16_t len);
+u16_t inet_chksum_pbuf(struct pbuf *p);
+#if LWIP_CHKSUM_COPY_ALGORITHM
+u16_t lwip_chksum_copy(void *dst, const void *src, u16_t len);
+#endif /* LWIP_CHKSUM_COPY_ALGORITHM */
+
+#if LWIP_IPV4
+u16_t inet_chksum_pseudo(struct pbuf *p, u8_t proto, u16_t proto_len,
+ const ip4_addr_t *src, const ip4_addr_t *dest);
+u16_t inet_chksum_pseudo_partial(struct pbuf *p, u8_t proto,
+ u16_t proto_len, u16_t chksum_len, const ip4_addr_t *src, const ip4_addr_t *dest);
+#endif /* LWIP_IPV4 */
+
+#if LWIP_IPV6
+u16_t ip6_chksum_pseudo(struct pbuf *p, u8_t proto, u16_t proto_len,
+ const ip6_addr_t *src, const ip6_addr_t *dest);
+u16_t ip6_chksum_pseudo_partial(struct pbuf *p, u8_t proto, u16_t proto_len,
+ u16_t chksum_len, const ip6_addr_t *src, const ip6_addr_t *dest);
+#endif /* LWIP_IPV6 */
+
+
+u16_t ip_chksum_pseudo(struct pbuf *p, u8_t proto, u16_t proto_len,
+ const ip_addr_t *src, const ip_addr_t *dest);
+u16_t ip_chksum_pseudo_partial(struct pbuf *p, u8_t proto, u16_t proto_len,
+ u16_t chksum_len, const ip_addr_t *src, const ip_addr_t *dest);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_INET_H */
+
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/init.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/init.h
new file mode 100644
index 0000000..940fa21
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/init.h
@@ -0,0 +1,100 @@
+/**
+ * @file
+ * lwIP initialization API
+ */
+
+/*
+ * 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_HDR_INIT_H
+#define LWIP_HDR_INIT_H
+
+#include "lwip/opt.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @defgroup lwip_version Version
+ * @ingroup lwip
+ * @{
+ */
+
+/** X.x.x: Major version of the stack */
+#define LWIP_VERSION_MAJOR 2
+/** x.X.x: Minor version of the stack */
+#define LWIP_VERSION_MINOR 0
+/** x.x.X: Revision of the stack */
+#define LWIP_VERSION_REVISION 2
+/** For release candidates, this is set to 1..254
+ * For official releases, this is set to 255 (LWIP_RC_RELEASE)
+ * For development versions (Git), this is set to 0 (LWIP_RC_DEVELOPMENT) */
+#define LWIP_VERSION_RC LWIP_RC_RELEASE
+
+/** LWIP_VERSION_RC is set to LWIP_RC_RELEASE for official releases */
+#define LWIP_RC_RELEASE 255
+/** LWIP_VERSION_RC is set to LWIP_RC_DEVELOPMENT for Git versions */
+#define LWIP_RC_DEVELOPMENT 0
+
+#define LWIP_VERSION_IS_RELEASE (LWIP_VERSION_RC == LWIP_RC_RELEASE)
+#define LWIP_VERSION_IS_DEVELOPMENT (LWIP_VERSION_RC == LWIP_RC_DEVELOPMENT)
+#define LWIP_VERSION_IS_RC ((LWIP_VERSION_RC != LWIP_RC_RELEASE) && (LWIP_VERSION_RC != LWIP_RC_DEVELOPMENT))
+
+/* Some helper defines to get a version string */
+#define LWIP_VERSTR2(x) #x
+#define LWIP_VERSTR(x) LWIP_VERSTR2(x)
+#if LWIP_VERSION_IS_RELEASE
+#define LWIP_VERSION_STRING_SUFFIX ""
+#elif LWIP_VERSION_IS_DEVELOPMENT
+#define LWIP_VERSION_STRING_SUFFIX "d"
+#else
+#define LWIP_VERSION_STRING_SUFFIX "rc" LWIP_VERSTR(LWIP_VERSION_RC)
+#endif
+
+/** Provides the version of the stack */
+#define LWIP_VERSION (((u32_t)LWIP_VERSION_MAJOR) << 24 | ((u32_t)LWIP_VERSION_MINOR) << 16 | \
+ ((u32_t)LWIP_VERSION_REVISION) << 8 | ((u32_t)LWIP_VERSION_RC))
+/** Provides the version of the stack as string */
+#define LWIP_VERSION_STRING LWIP_VERSTR(LWIP_VERSION_MAJOR) "." LWIP_VERSTR(LWIP_VERSION_MINOR) "." LWIP_VERSTR(LWIP_VERSION_REVISION) LWIP_VERSION_STRING_SUFFIX
+
+/**
+ * @}
+ */
+
+/* Modules initialization */
+void lwip_init(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_INIT_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/ip.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/ip.h
new file mode 100644
index 0000000..c8c29e5
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/ip.h
@@ -0,0 +1,321 @@
+/**
+ * @file
+ * IP API
+ */
+
+/*
+ * 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_HDR_IP_H
+#define LWIP_HDR_IP_H
+
+#include "lwip/opt.h"
+
+#include "lwip/def.h"
+#include "lwip/pbuf.h"
+#include "lwip/ip_addr.h"
+#include "lwip/err.h"
+#include "lwip/netif.h"
+#include "lwip/ip4.h"
+#include "lwip/ip6.h"
+#include "lwip/prot/ip.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* 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. */
+#define LWIP_IP_HDRINCL NULL
+
+/** pbufs passed to IP must have a ref-count of 1 as their payload pointer
+ gets altered as the packet is passed down the stack */
+#ifndef LWIP_IP_CHECK_PBUF_REF_COUNT_FOR_TX
+/*lint -save -e607 */
+#define LWIP_IP_CHECK_PBUF_REF_COUNT_FOR_TX(p) LWIP_ASSERT("p->ref == 1", (p)->ref == 1)
+/*lint -restore */
+#endif
+
+#if LWIP_NETIF_HWADDRHINT
+#define IP_PCB_ADDRHINT ;u8_t addr_hint
+#else
+#define IP_PCB_ADDRHINT
+#endif /* LWIP_NETIF_HWADDRHINT */
+
+/** This is the common part of all PCB types. It needs to be at the
+ beginning of a PCB type definition. It is located here so that
+ changes to this common part are made in one location instead of
+ having to change all PCB structs. */
+#define IP_PCB \
+ /* ip addresses in network byte order */ \
+ ip_addr_t local_ip; \
+ ip_addr_t remote_ip; \
+ /* Socket options */ \
+ u8_t so_options; \
+ /* Type Of Service */ \
+ u8_t tos; \
+ /* Time To Live */ \
+ u8_t ttl \
+ /* link layer address resolution hint */ \
+ IP_PCB_ADDRHINT
+
+struct ip_pcb {
+/* Common members of all PCB types */
+ IP_PCB;
+};
+
+/*
+ * Option flags per-socket. These are the same like SO_XXX in sockets.h
+ */
+#define SOF_REUSEADDR 0x04U /* allow local address reuse */
+#define SOF_KEEPALIVE 0x08U /* keep connections alive */
+#define SOF_BROADCAST 0x20U /* permit to send and to receive broadcast messages (see IP_SOF_BROADCAST option) */
+
+/* These flags are inherited (e.g. from a listen-pcb to a connection-pcb): */
+#define SOF_INHERITED (SOF_REUSEADDR|SOF_KEEPALIVE)
+
+/** Global variables of this module, kept in a struct for efficient access using base+index. */
+struct ip_globals
+{
+ /** The interface that accepted the packet for the current callback invocation. */
+ struct netif *current_netif;
+ /** The interface that received the packet for the current callback invocation. */
+ struct netif *current_input_netif;
+#if LWIP_IPV4
+ /** Header of the input packet currently being processed. */
+ struct ip_hdr *current_ip4_header;
+#endif /* LWIP_IPV4 */
+#if LWIP_IPV6
+ /** Header of the input IPv6 packet currently being processed. */
+ struct ip6_hdr *current_ip6_header;
+#endif /* LWIP_IPV6 */
+ /** Total header length of current_ip4/6_header (i.e. after this, the UDP/TCP header starts) */
+ u16_t current_ip_header_tot_len;
+ /** Source IP address of current_header */
+ ip_addr_t current_iphdr_src;
+ /** Destination IP address of current_header */
+ ip_addr_t current_iphdr_dest;
+};
+extern struct ip_globals ip_data;
+
+
+/** Get the interface that accepted the current packet.
+ * This may or may not be the receiving netif, depending on your netif/network setup.
+ * This function must only be called from a receive callback (udp_recv,
+ * raw_recv, tcp_accept). It will return NULL otherwise. */
+#define ip_current_netif() (ip_data.current_netif)
+/** Get the interface that received the current packet.
+ * This function must only be called from a receive callback (udp_recv,
+ * raw_recv, tcp_accept). It will return NULL otherwise. */
+#define ip_current_input_netif() (ip_data.current_input_netif)
+/** Total header length of ip(6)_current_header() (i.e. after this, the UDP/TCP header starts) */
+#define ip_current_header_tot_len() (ip_data.current_ip_header_tot_len)
+/** Source IP address of current_header */
+#define ip_current_src_addr() (&ip_data.current_iphdr_src)
+/** Destination IP address of current_header */
+#define ip_current_dest_addr() (&ip_data.current_iphdr_dest)
+
+#if LWIP_IPV4 && LWIP_IPV6
+/** Get the IPv4 header of the current packet.
+ * This function must only be called from a receive callback (udp_recv,
+ * raw_recv, tcp_accept). It will return NULL otherwise. */
+#define ip4_current_header() ((const struct ip_hdr*)(ip_data.current_ip4_header))
+/** Get the IPv6 header of the current packet.
+ * This function must only be called from a receive callback (udp_recv,
+ * raw_recv, tcp_accept). It will return NULL otherwise. */
+#define ip6_current_header() ((const struct ip6_hdr*)(ip_data.current_ip6_header))
+/** Returns TRUE if the current IP input packet is IPv6, FALSE if it is IPv4 */
+#define ip_current_is_v6() (ip6_current_header() != NULL)
+/** Source IPv6 address of current_header */
+#define ip6_current_src_addr() (ip_2_ip6(&ip_data.current_iphdr_src))
+/** Destination IPv6 address of current_header */
+#define ip6_current_dest_addr() (ip_2_ip6(&ip_data.current_iphdr_dest))
+/** Get the transport layer protocol */
+#define ip_current_header_proto() (ip_current_is_v6() ? \
+ IP6H_NEXTH(ip6_current_header()) :\
+ IPH_PROTO(ip4_current_header()))
+/** Get the transport layer header */
+#define ip_next_header_ptr() ((const void*)((ip_current_is_v6() ? \
+ (const u8_t*)ip6_current_header() : (const u8_t*)ip4_current_header()) + ip_current_header_tot_len()))
+
+/** Source IP4 address of current_header */
+#define ip4_current_src_addr() (ip_2_ip4(&ip_data.current_iphdr_src))
+/** Destination IP4 address of current_header */
+#define ip4_current_dest_addr() (ip_2_ip4(&ip_data.current_iphdr_dest))
+
+#elif LWIP_IPV4 /* LWIP_IPV4 && LWIP_IPV6 */
+
+/** Get the IPv4 header of the current packet.
+ * This function must only be called from a receive callback (udp_recv,
+ * raw_recv, tcp_accept). It will return NULL otherwise. */
+#define ip4_current_header() ((const struct ip_hdr*)(ip_data.current_ip4_header))
+/** Always returns FALSE when only supporting IPv4 only */
+#define ip_current_is_v6() 0
+/** Get the transport layer protocol */
+#define ip_current_header_proto() IPH_PROTO(ip4_current_header())
+/** Get the transport layer header */
+#define ip_next_header_ptr() ((const void*)((const u8_t*)ip4_current_header() + ip_current_header_tot_len()))
+/** Source IP4 address of current_header */
+#define ip4_current_src_addr() (&ip_data.current_iphdr_src)
+/** Destination IP4 address of current_header */
+#define ip4_current_dest_addr() (&ip_data.current_iphdr_dest)
+
+#elif LWIP_IPV6 /* LWIP_IPV4 && LWIP_IPV6 */
+
+/** Get the IPv6 header of the current packet.
+ * This function must only be called from a receive callback (udp_recv,
+ * raw_recv, tcp_accept). It will return NULL otherwise. */
+#define ip6_current_header() ((const struct ip6_hdr*)(ip_data.current_ip6_header))
+/** Always returns TRUE when only supporting IPv6 only */
+#define ip_current_is_v6() 1
+/** Get the transport layer protocol */
+#define ip_current_header_proto() IP6H_NEXTH(ip6_current_header())
+/** Get the transport layer header */
+#define ip_next_header_ptr() ((const void*)((const u8_t*)ip6_current_header()))
+/** Source IP6 address of current_header */
+#define ip6_current_src_addr() (&ip_data.current_iphdr_src)
+/** Destination IP6 address of current_header */
+#define ip6_current_dest_addr() (&ip_data.current_iphdr_dest)
+
+#endif /* LWIP_IPV6 */
+
+/** Union source address of current_header */
+#define ip_current_src_addr() (&ip_data.current_iphdr_src)
+/** Union destination address of current_header */
+#define ip_current_dest_addr() (&ip_data.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 LWIP_IPV4 && LWIP_IPV6
+/**
+ * @ingroup ip
+ * Output IP packet, netif is selected by source address
+ */
+#define ip_output(p, src, dest, ttl, tos, proto) \
+ (IP_IS_V6(dest) ? \
+ ip6_output(p, ip_2_ip6(src), ip_2_ip6(dest), ttl, tos, proto) : \
+ ip4_output(p, ip_2_ip4(src), ip_2_ip4(dest), ttl, tos, proto))
+/**
+ * @ingroup ip
+ * Output IP packet to specified interface
+ */
+#define ip_output_if(p, src, dest, ttl, tos, proto, netif) \
+ (IP_IS_V6(dest) ? \
+ ip6_output_if(p, ip_2_ip6(src), ip_2_ip6(dest), ttl, tos, proto, netif) : \
+ ip4_output_if(p, ip_2_ip4(src), ip_2_ip4(dest), ttl, tos, proto, netif))
+/**
+ * @ingroup ip
+ * Output IP packet to interface specifying source address
+ */
+#define ip_output_if_src(p, src, dest, ttl, tos, proto, netif) \
+ (IP_IS_V6(dest) ? \
+ ip6_output_if_src(p, ip_2_ip6(src), ip_2_ip6(dest), ttl, tos, proto, netif) : \
+ ip4_output_if_src(p, ip_2_ip4(src), ip_2_ip4(dest), ttl, tos, proto, netif))
+/** Output IP packet with addr_hint */
+#define ip_output_hinted(p, src, dest, ttl, tos, proto, addr_hint) \
+ (IP_IS_V6(dest) ? \
+ ip6_output_hinted(p, ip_2_ip6(src), ip_2_ip6(dest), ttl, tos, proto, addr_hint) : \
+ ip4_output_hinted(p, ip_2_ip4(src), ip_2_ip4(dest), ttl, tos, proto, addr_hint))
+/**
+ * @ingroup ip
+ * Get netif for address combination. See \ref ip6_route and \ref ip4_route
+ */
+#define ip_route(src, dest) \
+ (IP_IS_V6(dest) ? \
+ ip6_route(ip_2_ip6(src), ip_2_ip6(dest)) : \
+ ip4_route_src(ip_2_ip4(dest), ip_2_ip4(src)))
+/**
+ * @ingroup ip
+ * Get netif for IP.
+ */
+#define ip_netif_get_local_ip(netif, dest) (IP_IS_V6(dest) ? \
+ ip6_netif_get_local_ip(netif, ip_2_ip6(dest)) : \
+ ip4_netif_get_local_ip(netif))
+#define ip_debug_print(is_ipv6, p) ((is_ipv6) ? ip6_debug_print(p) : ip4_debug_print(p))
+
+err_t ip_input(struct pbuf *p, struct netif *inp);
+
+#elif LWIP_IPV4 /* LWIP_IPV4 && LWIP_IPV6 */
+
+#define ip_output(p, src, dest, ttl, tos, proto) \
+ ip4_output(p, src, dest, ttl, tos, proto)
+#define ip_output_if(p, src, dest, ttl, tos, proto, netif) \
+ ip4_output_if(p, src, dest, ttl, tos, proto, netif)
+#define ip_output_if_src(p, src, dest, ttl, tos, proto, netif) \
+ ip4_output_if_src(p, src, dest, ttl, tos, proto, netif)
+#define ip_output_hinted(p, src, dest, ttl, tos, proto, addr_hint) \
+ ip4_output_hinted(p, src, dest, ttl, tos, proto, addr_hint)
+#define ip_route(src, dest) \
+ ip4_route_src(dest, src)
+#define ip_netif_get_local_ip(netif, dest) \
+ ip4_netif_get_local_ip(netif)
+#define ip_debug_print(is_ipv6, p) ip4_debug_print(p)
+
+#define ip_input ip4_input
+
+#elif LWIP_IPV6 /* LWIP_IPV4 && LWIP_IPV6 */
+
+#define ip_output(p, src, dest, ttl, tos, proto) \
+ ip6_output(p, src, dest, ttl, tos, proto)
+#define ip_output_if(p, src, dest, ttl, tos, proto, netif) \
+ ip6_output_if(p, src, dest, ttl, tos, proto, netif)
+#define ip_output_if_src(p, src, dest, ttl, tos, proto, netif) \
+ ip6_output_if_src(p, src, dest, ttl, tos, proto, netif)
+#define ip_output_hinted(p, src, dest, ttl, tos, proto, addr_hint) \
+ ip6_output_hinted(p, src, dest, ttl, tos, proto, addr_hint)
+#define ip_route(src, dest) \
+ ip6_route(src, dest)
+#define ip_netif_get_local_ip(netif, dest) \
+ ip6_netif_get_local_ip(netif, dest)
+#define ip_debug_print(is_ipv6, p) ip6_debug_print(p)
+
+#define ip_input ip6_input
+
+#endif /* LWIP_IPV6 */
+
+#define ip_route_get_local_ip(src, dest, netif, ipaddr) do { \
+ (netif) = ip_route(src, dest); \
+ (ipaddr) = ip_netif_get_local_ip(netif, dest); \
+}while(0)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_IP_H */
+
+
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/ip4.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/ip4.h
new file mode 100644
index 0000000..48246ec
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/ip4.h
@@ -0,0 +1,111 @@
+/**
+ * @file
+ * IPv4 API
+ */
+
+/*
+ * 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_HDR_IP4_H
+#define LWIP_HDR_IP4_H
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV4
+
+#include "lwip/def.h"
+#include "lwip/pbuf.h"
+#include "lwip/ip4_addr.h"
+#include "lwip/err.h"
+#include "lwip/netif.h"
+#include "lwip/prot/ip4.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef LWIP_HOOK_IP4_ROUTE_SRC
+#define LWIP_IPV4_SRC_ROUTING 1
+#else
+#define LWIP_IPV4_SRC_ROUTING 0
+#endif
+
+/** Currently, the function ip_output_if_opt() is only used with IGMP */
+#define IP_OPTIONS_SEND (LWIP_IPV4 && LWIP_IGMP)
+
+#define ip_init() /* Compatibility define, no init needed. */
+struct netif *ip4_route(const ip4_addr_t *dest);
+#if LWIP_IPV4_SRC_ROUTING
+struct netif *ip4_route_src(const ip4_addr_t *dest, const ip4_addr_t *src);
+#else /* LWIP_IPV4_SRC_ROUTING */
+#define ip4_route_src(dest, src) ip4_route(dest)
+#endif /* LWIP_IPV4_SRC_ROUTING */
+err_t ip4_input(struct pbuf *p, struct netif *inp);
+err_t ip4_output(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest,
+ u8_t ttl, u8_t tos, u8_t proto);
+err_t ip4_output_if(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest,
+ u8_t ttl, u8_t tos, u8_t proto, struct netif *netif);
+err_t ip4_output_if_src(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest,
+ u8_t ttl, u8_t tos, u8_t proto, struct netif *netif);
+#if LWIP_NETIF_HWADDRHINT
+err_t ip4_output_hinted(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest,
+ u8_t ttl, u8_t tos, u8_t proto, u8_t *addr_hint);
+#endif /* LWIP_NETIF_HWADDRHINT */
+#if IP_OPTIONS_SEND
+err_t ip4_output_if_opt(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest,
+ u8_t ttl, u8_t tos, u8_t proto, struct netif *netif, void *ip_options,
+ u16_t optlen);
+err_t ip4_output_if_opt_src(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest,
+ u8_t ttl, u8_t tos, u8_t proto, struct netif *netif, void *ip_options,
+ u16_t optlen);
+#endif /* IP_OPTIONS_SEND */
+
+#if LWIP_MULTICAST_TX_OPTIONS
+void ip4_set_default_multicast_netif(struct netif* default_multicast_netif);
+#endif /* LWIP_MULTICAST_TX_OPTIONS */
+
+#define ip4_netif_get_local_ip(netif) (((netif) != NULL) ? netif_ip_addr4(netif) : NULL)
+
+#if IP_DEBUG
+void ip4_debug_print(struct pbuf *p);
+#else
+#define ip4_debug_print(p)
+#endif /* IP_DEBUG */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_IPV4 */
+
+#endif /* LWIP_HDR_IP_H */
+
+
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/ip4_addr.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/ip4_addr.h
new file mode 100644
index 0000000..51b46b8
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/ip4_addr.h
@@ -0,0 +1,227 @@
+/**
+ * @file
+ * IPv4 address API
+ */
+
+/*
+ * 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_HDR_IP4_ADDR_H
+#define LWIP_HDR_IP4_ADDR_H
+
+#include "lwip/opt.h"
+#include "lwip/def.h"
+
+#if LWIP_IPV4
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** This is the aligned version of ip4_addr_t,
+ used as local variable, on the stack, etc. */
+struct ip4_addr {
+ u32_t addr;
+};
+
+/** ip4_addr_t uses a struct for convenience only, so that the same defines can
+ * operate both on ip4_addr_t as well as on ip4_addr_p_t. */
+typedef struct ip4_addr ip4_addr_t;
+
+/**
+ * struct ipaddr2 is used in the definition of the ARP packet format in
+ * order to support compilers that don't have structure packing.
+ */
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct ip4_addr2 {
+ PACK_STRUCT_FIELD(u16_t addrw[2]);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+/* Forward declaration to not include netif.h */
+struct netif;
+
+/** 255.255.255.255 */
+#define IPADDR_NONE ((u32_t)0xffffffffUL)
+/** 127.0.0.1 */
+#define IPADDR_LOOPBACK ((u32_t)0x7f000001UL)
+/** 0.0.0.0 */
+#define IPADDR_ANY ((u32_t)0x00000000UL)
+/** 255.255.255.255 */
+#define IPADDR_BROADCAST ((u32_t)0xffffffffUL)
+
+/* Definitions of the bits in an Internet address integer.
+
+ On subnets, host and network parts are found according to
+ the subnet mask, not these masks. */
+#define IP_CLASSA(a) ((((u32_t)(a)) & 0x80000000UL) == 0)
+#define IP_CLASSA_NET 0xff000000
+#define IP_CLASSA_NSHIFT 24
+#define IP_CLASSA_HOST (0xffffffff & ~IP_CLASSA_NET)
+#define IP_CLASSA_MAX 128
+
+#define IP_CLASSB(a) ((((u32_t)(a)) & 0xc0000000UL) == 0x80000000UL)
+#define IP_CLASSB_NET 0xffff0000
+#define IP_CLASSB_NSHIFT 16
+#define IP_CLASSB_HOST (0xffffffff & ~IP_CLASSB_NET)
+#define IP_CLASSB_MAX 65536
+
+#define IP_CLASSC(a) ((((u32_t)(a)) & 0xe0000000UL) == 0xc0000000UL)
+#define IP_CLASSC_NET 0xffffff00
+#define IP_CLASSC_NSHIFT 8
+#define IP_CLASSC_HOST (0xffffffff & ~IP_CLASSC_NET)
+
+#define IP_CLASSD(a) (((u32_t)(a) & 0xf0000000UL) == 0xe0000000UL)
+#define IP_CLASSD_NET 0xf0000000 /* These ones aren't really */
+#define IP_CLASSD_NSHIFT 28 /* net and host fields, but */
+#define IP_CLASSD_HOST 0x0fffffff /* routing needn't know. */
+#define IP_MULTICAST(a) IP_CLASSD(a)
+
+#define IP_EXPERIMENTAL(a) (((u32_t)(a) & 0xf0000000UL) == 0xf0000000UL)
+#define IP_BADCLASS(a) (((u32_t)(a) & 0xf0000000UL) == 0xf0000000UL)
+
+#define IP_LOOPBACKNET 127 /* official! */
+
+/** Set an IP address given by the four byte-parts */
+#define IP4_ADDR(ipaddr, a,b,c,d) (ipaddr)->addr = PP_HTONL(LWIP_MAKEU32(a,b,c,d))
+
+/** MEMCPY-like copying of IP addresses where addresses are known to be
+ * 16-bit-aligned if the port is correctly configured (so a port could define
+ * this to copying 2 u16_t's) - no NULL-pointer-checking needed. */
+#ifndef IPADDR2_COPY
+#define IPADDR2_COPY(dest, src) SMEMCPY(dest, src, sizeof(ip4_addr_t))
+#endif
+
+/** Copy IP address - faster than ip4_addr_set: no NULL check */
+#define ip4_addr_copy(dest, src) ((dest).addr = (src).addr)
+/** Safely copy one IP address to another (src may be NULL) */
+#define ip4_addr_set(dest, src) ((dest)->addr = \
+ ((src) == NULL ? 0 : \
+ (src)->addr))
+/** Set complete address to zero */
+#define ip4_addr_set_zero(ipaddr) ((ipaddr)->addr = 0)
+/** Set address to IPADDR_ANY (no need for lwip_htonl()) */
+#define ip4_addr_set_any(ipaddr) ((ipaddr)->addr = IPADDR_ANY)
+/** Set address to loopback address */
+#define ip4_addr_set_loopback(ipaddr) ((ipaddr)->addr = PP_HTONL(IPADDR_LOOPBACK))
+/** Check if an address is in the loopback region */
+#define ip4_addr_isloopback(ipaddr) (((ipaddr)->addr & PP_HTONL(IP_CLASSA_NET)) == PP_HTONL(((u32_t)IP_LOOPBACKNET) << 24))
+/** Safely copy one IP address to another and change byte order
+ * from host- to network-order. */
+#define ip4_addr_set_hton(dest, src) ((dest)->addr = \
+ ((src) == NULL ? 0:\
+ lwip_htonl((src)->addr)))
+/** IPv4 only: set the IP address given as an u32_t */
+#define ip4_addr_set_u32(dest_ipaddr, src_u32) ((dest_ipaddr)->addr = (src_u32))
+/** IPv4 only: get the IP address as an u32_t */
+#define ip4_addr_get_u32(src_ipaddr) ((src_ipaddr)->addr)
+
+/** Get the network address by combining host address with netmask */
+#define ip4_addr_get_network(target, host, netmask) do { ((target)->addr = ((host)->addr) & ((netmask)->addr)); } while(0)
+
+/**
+ * Determine if two address are on the same network.
+ *
+ * @arg addr1 IP address 1
+ * @arg addr2 IP address 2
+ * @arg mask network identifier mask
+ * @return !0 if the network identifiers of both address match
+ */
+#define ip4_addr_netcmp(addr1, addr2, mask) (((addr1)->addr & \
+ (mask)->addr) == \
+ ((addr2)->addr & \
+ (mask)->addr))
+#define ip4_addr_cmp(addr1, addr2) ((addr1)->addr == (addr2)->addr)
+
+#define ip4_addr_isany_val(addr1) ((addr1).addr == IPADDR_ANY)
+#define ip4_addr_isany(addr1) ((addr1) == NULL || ip4_addr_isany_val(*(addr1)))
+
+#define ip4_addr_isbroadcast(addr1, netif) ip4_addr_isbroadcast_u32((addr1)->addr, netif)
+u8_t ip4_addr_isbroadcast_u32(u32_t addr, const struct netif *netif);
+
+#define ip_addr_netmask_valid(netmask) ip4_addr_netmask_valid((netmask)->addr)
+u8_t ip4_addr_netmask_valid(u32_t netmask);
+
+#define ip4_addr_ismulticast(addr1) (((addr1)->addr & PP_HTONL(0xf0000000UL)) == PP_HTONL(0xe0000000UL))
+
+#define ip4_addr_islinklocal(addr1) (((addr1)->addr & PP_HTONL(0xffff0000UL)) == PP_HTONL(0xa9fe0000UL))
+
+#define ip4_addr_debug_print_parts(debug, a, b, c, d) \
+ LWIP_DEBUGF(debug, ("%" U16_F ".%" U16_F ".%" U16_F ".%" U16_F, a, b, c, d))
+#define ip4_addr_debug_print(debug, ipaddr) \
+ ip4_addr_debug_print_parts(debug, \
+ (u16_t)((ipaddr) != NULL ? ip4_addr1_16(ipaddr) : 0), \
+ (u16_t)((ipaddr) != NULL ? ip4_addr2_16(ipaddr) : 0), \
+ (u16_t)((ipaddr) != NULL ? ip4_addr3_16(ipaddr) : 0), \
+ (u16_t)((ipaddr) != NULL ? ip4_addr4_16(ipaddr) : 0))
+#define ip4_addr_debug_print_val(debug, ipaddr) \
+ ip4_addr_debug_print_parts(debug, \
+ ip4_addr1_16(&(ipaddr)), \
+ ip4_addr2_16(&(ipaddr)), \
+ ip4_addr3_16(&(ipaddr)), \
+ ip4_addr4_16(&(ipaddr)))
+
+/* Get one byte from the 4-byte address */
+#define ip4_addr1(ipaddr) (((const u8_t*)(&(ipaddr)->addr))[0])
+#define ip4_addr2(ipaddr) (((const u8_t*)(&(ipaddr)->addr))[1])
+#define ip4_addr3(ipaddr) (((const u8_t*)(&(ipaddr)->addr))[2])
+#define ip4_addr4(ipaddr) (((const u8_t*)(&(ipaddr)->addr))[3])
+/* These are cast to u16_t, with the intent that they are often arguments
+ * to printf using the U16_F format from cc.h. */
+#define ip4_addr1_16(ipaddr) ((u16_t)ip4_addr1(ipaddr))
+#define ip4_addr2_16(ipaddr) ((u16_t)ip4_addr2(ipaddr))
+#define ip4_addr3_16(ipaddr) ((u16_t)ip4_addr3(ipaddr))
+#define ip4_addr4_16(ipaddr) ((u16_t)ip4_addr4(ipaddr))
+
+#define IP4ADDR_STRLEN_MAX 16
+
+/** For backwards compatibility */
+#define ip_ntoa(ipaddr) ipaddr_ntoa(ipaddr)
+
+u32_t ipaddr_addr(const char *cp);
+int ip4addr_aton(const char *cp, ip4_addr_t *addr);
+/** returns ptr to static buffer; not reentrant! */
+char *ip4addr_ntoa(const ip4_addr_t *addr);
+char *ip4addr_ntoa_r(const ip4_addr_t *addr, char *buf, int buflen);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_IPV4 */
+
+#endif /* LWIP_HDR_IP_ADDR_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/ip4_frag.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/ip4_frag.h
new file mode 100644
index 0000000..ed5bf14
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/ip4_frag.h
@@ -0,0 +1,100 @@
+/**
+ * @file
+ * IP fragmentation/reassembly
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Jani Monoses <jani@iv.ro>
+ *
+ */
+
+#ifndef LWIP_HDR_IP4_FRAG_H
+#define LWIP_HDR_IP4_FRAG_H
+
+#include "lwip/opt.h"
+#include "lwip/err.h"
+#include "lwip/pbuf.h"
+#include "lwip/netif.h"
+#include "lwip/ip_addr.h"
+#include "lwip/ip.h"
+
+#if LWIP_IPV4
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if IP_REASSEMBLY
+/* The IP reassembly timer interval in milliseconds. */
+#define IP_TMR_INTERVAL 1000
+
+/** IP reassembly helper struct.
+ * This is exported because memp needs to know the size.
+ */
+struct ip_reassdata {
+ struct ip_reassdata *next;
+ struct pbuf *p;
+ struct ip_hdr iphdr;
+ u16_t datagram_len;
+ u8_t flags;
+ u8_t timer;
+};
+
+void ip_reass_init(void);
+void ip_reass_tmr(void);
+struct pbuf * ip4_reass(struct pbuf *p);
+#endif /* IP_REASSEMBLY */
+
+#if IP_FRAG
+#if !LWIP_NETIF_TX_SINGLE_PBUF
+#ifndef LWIP_PBUF_CUSTOM_REF_DEFINED
+#define LWIP_PBUF_CUSTOM_REF_DEFINED
+/** A custom pbuf that holds a reference to another pbuf, which is freed
+ * when this custom pbuf is freed. This is used to create a custom PBUF_REF
+ * that points into the original pbuf. */
+struct pbuf_custom_ref {
+ /** 'base class' */
+ struct pbuf_custom pc;
+ /** pointer to the original pbuf that is referenced */
+ struct pbuf *original;
+};
+#endif /* LWIP_PBUF_CUSTOM_REF_DEFINED */
+#endif /* !LWIP_NETIF_TX_SINGLE_PBUF */
+
+err_t ip4_frag(struct pbuf *p, struct netif *netif, const ip4_addr_t *dest);
+#endif /* IP_FRAG */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_IPV4 */
+
+#endif /* LWIP_HDR_IP4_FRAG_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/ip6.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/ip6.h
new file mode 100644
index 0000000..099b94f
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/ip6.h
@@ -0,0 +1,93 @@
+/**
+ * @file
+ *
+ * IPv6 layer.
+ */
+
+/*
+ * Copyright (c) 2010 Inico Technologies Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Ivan Delamer <delamer@inicotech.com>
+ *
+ *
+ * Please coordinate changes and requests with Ivan Delamer
+ * <delamer@inicotech.com>
+ */
+#ifndef LWIP_HDR_IP6_H
+#define LWIP_HDR_IP6_H
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/ip6_addr.h"
+#include "lwip/prot/ip6.h"
+#include "lwip/def.h"
+#include "lwip/pbuf.h"
+#include "lwip/netif.h"
+
+#include "lwip/err.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct netif *ip6_route(const ip6_addr_t *src, const ip6_addr_t *dest);
+const ip_addr_t *ip6_select_source_address(struct netif *netif, const ip6_addr_t * dest);
+err_t ip6_input(struct pbuf *p, struct netif *inp);
+err_t ip6_output(struct pbuf *p, const ip6_addr_t *src, const ip6_addr_t *dest,
+ u8_t hl, u8_t tc, u8_t nexth);
+err_t ip6_output_if(struct pbuf *p, const ip6_addr_t *src, const ip6_addr_t *dest,
+ u8_t hl, u8_t tc, u8_t nexth, struct netif *netif);
+err_t ip6_output_if_src(struct pbuf *p, const ip6_addr_t *src, const ip6_addr_t *dest,
+ u8_t hl, u8_t tc, u8_t nexth, struct netif *netif);
+#if LWIP_NETIF_HWADDRHINT
+err_t ip6_output_hinted(struct pbuf *p, const ip6_addr_t *src, const ip6_addr_t *dest,
+ u8_t hl, u8_t tc, u8_t nexth, u8_t *addr_hint);
+#endif /* LWIP_NETIF_HWADDRHINT */
+#if LWIP_IPV6_MLD
+err_t ip6_options_add_hbh_ra(struct pbuf * p, u8_t nexth, u8_t value);
+#endif /* LWIP_IPV6_MLD */
+
+#define ip6_netif_get_local_ip(netif, dest) (((netif) != NULL) ? \
+ ip6_select_source_address(netif, dest) : NULL)
+
+#if IP6_DEBUG
+void ip6_debug_print(struct pbuf *p);
+#else
+#define ip6_debug_print(p)
+#endif /* IP6_DEBUG */
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_IPV6 */
+
+#endif /* LWIP_HDR_IP6_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/ip6_addr.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/ip6_addr.h
new file mode 100644
index 0000000..ee381ae
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/ip6_addr.h
@@ -0,0 +1,285 @@
+/**
+ * @file
+ *
+ * IPv6 addresses.
+ */
+
+/*
+ * Copyright (c) 2010 Inico Technologies Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Ivan Delamer <delamer@inicotech.com>
+ *
+ * Structs and macros for handling IPv6 addresses.
+ *
+ * Please coordinate changes and requests with Ivan Delamer
+ * <delamer@inicotech.com>
+ */
+#ifndef LWIP_HDR_IP6_ADDR_H
+#define LWIP_HDR_IP6_ADDR_H
+
+#include "lwip/opt.h"
+#include "def.h"
+
+#if LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/** This is the aligned version of ip6_addr_t,
+ used as local variable, on the stack, etc. */
+struct ip6_addr {
+ u32_t addr[4];
+};
+
+/** IPv6 address */
+typedef struct ip6_addr ip6_addr_t;
+
+/** Set an IPv6 partial address given by byte-parts */
+#define IP6_ADDR_PART(ip6addr, index, a,b,c,d) \
+ (ip6addr)->addr[index] = PP_HTONL(LWIP_MAKEU32(a,b,c,d))
+
+/** Set a full IPv6 address by passing the 4 u32_t indices in network byte order
+ (use PP_HTONL() for constants) */
+#define IP6_ADDR(ip6addr, idx0, idx1, idx2, idx3) do { \
+ (ip6addr)->addr[0] = idx0; \
+ (ip6addr)->addr[1] = idx1; \
+ (ip6addr)->addr[2] = idx2; \
+ (ip6addr)->addr[3] = idx3; } while(0)
+
+/** Access address in 16-bit block */
+#define IP6_ADDR_BLOCK1(ip6addr) ((u16_t)((lwip_htonl((ip6addr)->addr[0]) >> 16) & 0xffff))
+/** Access address in 16-bit block */
+#define IP6_ADDR_BLOCK2(ip6addr) ((u16_t)((lwip_htonl((ip6addr)->addr[0])) & 0xffff))
+/** Access address in 16-bit block */
+#define IP6_ADDR_BLOCK3(ip6addr) ((u16_t)((lwip_htonl((ip6addr)->addr[1]) >> 16) & 0xffff))
+/** Access address in 16-bit block */
+#define IP6_ADDR_BLOCK4(ip6addr) ((u16_t)((lwip_htonl((ip6addr)->addr[1])) & 0xffff))
+/** Access address in 16-bit block */
+#define IP6_ADDR_BLOCK5(ip6addr) ((u16_t)((lwip_htonl((ip6addr)->addr[2]) >> 16) & 0xffff))
+/** Access address in 16-bit block */
+#define IP6_ADDR_BLOCK6(ip6addr) ((u16_t)((lwip_htonl((ip6addr)->addr[2])) & 0xffff))
+/** Access address in 16-bit block */
+#define IP6_ADDR_BLOCK7(ip6addr) ((u16_t)((lwip_htonl((ip6addr)->addr[3]) >> 16) & 0xffff))
+/** Access address in 16-bit block */
+#define IP6_ADDR_BLOCK8(ip6addr) ((u16_t)((lwip_htonl((ip6addr)->addr[3])) & 0xffff))
+
+/** Copy IPv6 address - faster than ip6_addr_set: no NULL check */
+#define ip6_addr_copy(dest, src) do{(dest).addr[0] = (src).addr[0]; \
+ (dest).addr[1] = (src).addr[1]; \
+ (dest).addr[2] = (src).addr[2]; \
+ (dest).addr[3] = (src).addr[3];}while(0)
+/** Safely copy one IPv6 address to another (src may be NULL) */
+#define ip6_addr_set(dest, src) do{(dest)->addr[0] = (src) == NULL ? 0 : (src)->addr[0]; \
+ (dest)->addr[1] = (src) == NULL ? 0 : (src)->addr[1]; \
+ (dest)->addr[2] = (src) == NULL ? 0 : (src)->addr[2]; \
+ (dest)->addr[3] = (src) == NULL ? 0 : (src)->addr[3];}while(0)
+
+/** Set complete address to zero */
+#define ip6_addr_set_zero(ip6addr) do{(ip6addr)->addr[0] = 0; \
+ (ip6addr)->addr[1] = 0; \
+ (ip6addr)->addr[2] = 0; \
+ (ip6addr)->addr[3] = 0;}while(0)
+
+/** Set address to ipv6 'any' (no need for lwip_htonl()) */
+#define ip6_addr_set_any(ip6addr) ip6_addr_set_zero(ip6addr)
+/** Set address to ipv6 loopback address */
+#define ip6_addr_set_loopback(ip6addr) do{(ip6addr)->addr[0] = 0; \
+ (ip6addr)->addr[1] = 0; \
+ (ip6addr)->addr[2] = 0; \
+ (ip6addr)->addr[3] = PP_HTONL(0x00000001UL);}while(0)
+/** Safely copy one IPv6 address to another and change byte order
+ * from host- to network-order. */
+#define ip6_addr_set_hton(dest, src) do{(dest)->addr[0] = (src) == NULL ? 0 : lwip_htonl((src)->addr[0]); \
+ (dest)->addr[1] = (src) == NULL ? 0 : lwip_htonl((src)->addr[1]); \
+ (dest)->addr[2] = (src) == NULL ? 0 : lwip_htonl((src)->addr[2]); \
+ (dest)->addr[3] = (src) == NULL ? 0 : lwip_htonl((src)->addr[3]);}while(0)
+
+
+/**
+ * Determine if two IPv6 address are on the same network.
+ *
+ * @arg addr1 IPv6 address 1
+ * @arg addr2 IPv6 address 2
+ * @return !0 if the network identifiers of both address match
+ */
+#define ip6_addr_netcmp(addr1, addr2) (((addr1)->addr[0] == (addr2)->addr[0]) && \
+ ((addr1)->addr[1] == (addr2)->addr[1]))
+
+#define ip6_addr_cmp(addr1, addr2) (((addr1)->addr[0] == (addr2)->addr[0]) && \
+ ((addr1)->addr[1] == (addr2)->addr[1]) && \
+ ((addr1)->addr[2] == (addr2)->addr[2]) && \
+ ((addr1)->addr[3] == (addr2)->addr[3]))
+
+#define ip6_get_subnet_id(ip6addr) (lwip_htonl((ip6addr)->addr[2]) & 0x0000ffffUL)
+
+#define ip6_addr_isany_val(ip6addr) (((ip6addr).addr[0] == 0) && \
+ ((ip6addr).addr[1] == 0) && \
+ ((ip6addr).addr[2] == 0) && \
+ ((ip6addr).addr[3] == 0))
+#define ip6_addr_isany(ip6addr) (((ip6addr) == NULL) || ip6_addr_isany_val(*(ip6addr)))
+
+#define ip6_addr_isloopback(ip6addr) (((ip6addr)->addr[0] == 0UL) && \
+ ((ip6addr)->addr[1] == 0UL) && \
+ ((ip6addr)->addr[2] == 0UL) && \
+ ((ip6addr)->addr[3] == PP_HTONL(0x00000001UL)))
+
+#define ip6_addr_isglobal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xe0000000UL)) == PP_HTONL(0x20000000UL))
+
+#define ip6_addr_islinklocal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xffc00000UL)) == PP_HTONL(0xfe800000UL))
+
+#define ip6_addr_issitelocal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xffc00000UL)) == PP_HTONL(0xfec00000UL))
+
+#define ip6_addr_isuniquelocal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xfe000000UL)) == PP_HTONL(0xfc000000UL))
+
+#define ip6_addr_isipv4mappedipv6(ip6addr) (((ip6addr)->addr[0] == 0) && ((ip6addr)->addr[1] == 0) && (((ip6addr)->addr[2]) == PP_HTONL(0x0000FFFFUL)))
+
+#define ip6_addr_ismulticast(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xff000000UL)) == PP_HTONL(0xff000000UL))
+#define ip6_addr_multicast_transient_flag(ip6addr) ((ip6addr)->addr[0] & PP_HTONL(0x00100000UL))
+#define ip6_addr_multicast_prefix_flag(ip6addr) ((ip6addr)->addr[0] & PP_HTONL(0x00200000UL))
+#define ip6_addr_multicast_rendezvous_flag(ip6addr) ((ip6addr)->addr[0] & PP_HTONL(0x00400000UL))
+#define ip6_addr_multicast_scope(ip6addr) ((lwip_htonl((ip6addr)->addr[0]) >> 16) & 0xf)
+#define IP6_MULTICAST_SCOPE_RESERVED 0x0
+#define IP6_MULTICAST_SCOPE_RESERVED0 0x0
+#define IP6_MULTICAST_SCOPE_INTERFACE_LOCAL 0x1
+#define IP6_MULTICAST_SCOPE_LINK_LOCAL 0x2
+#define IP6_MULTICAST_SCOPE_RESERVED3 0x3
+#define IP6_MULTICAST_SCOPE_ADMIN_LOCAL 0x4
+#define IP6_MULTICAST_SCOPE_SITE_LOCAL 0x5
+#define IP6_MULTICAST_SCOPE_ORGANIZATION_LOCAL 0x8
+#define IP6_MULTICAST_SCOPE_GLOBAL 0xe
+#define IP6_MULTICAST_SCOPE_RESERVEDF 0xf
+#define ip6_addr_ismulticast_iflocal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xff8f0000UL)) == PP_HTONL(0xff010000UL))
+#define ip6_addr_ismulticast_linklocal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xff8f0000UL)) == PP_HTONL(0xff020000UL))
+#define ip6_addr_ismulticast_adminlocal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xff8f0000UL)) == PP_HTONL(0xff040000UL))
+#define ip6_addr_ismulticast_sitelocal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xff8f0000UL)) == PP_HTONL(0xff050000UL))
+#define ip6_addr_ismulticast_orglocal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xff8f0000UL)) == PP_HTONL(0xff080000UL))
+#define ip6_addr_ismulticast_global(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xff8f0000UL)) == PP_HTONL(0xff0e0000UL))
+
+/* @todo define get/set for well-know multicast addresses, e.g. ff02::1 */
+#define ip6_addr_isallnodes_iflocal(ip6addr) (((ip6addr)->addr[0] == PP_HTONL(0xff010000UL)) && \
+ ((ip6addr)->addr[1] == 0UL) && \
+ ((ip6addr)->addr[2] == 0UL) && \
+ ((ip6addr)->addr[3] == PP_HTONL(0x00000001UL)))
+
+#define ip6_addr_isallnodes_linklocal(ip6addr) (((ip6addr)->addr[0] == PP_HTONL(0xff020000UL)) && \
+ ((ip6addr)->addr[1] == 0UL) && \
+ ((ip6addr)->addr[2] == 0UL) && \
+ ((ip6addr)->addr[3] == PP_HTONL(0x00000001UL)))
+#define ip6_addr_set_allnodes_linklocal(ip6addr) do{(ip6addr)->addr[0] = PP_HTONL(0xff020000UL); \
+ (ip6addr)->addr[1] = 0; \
+ (ip6addr)->addr[2] = 0; \
+ (ip6addr)->addr[3] = PP_HTONL(0x00000001UL);}while(0)
+
+#define ip6_addr_isallrouters_linklocal(ip6addr) (((ip6addr)->addr[0] == PP_HTONL(0xff020000UL)) && \
+ ((ip6addr)->addr[1] == 0UL) && \
+ ((ip6addr)->addr[2] == 0UL) && \
+ ((ip6addr)->addr[3] == PP_HTONL(0x00000002UL)))
+#define ip6_addr_set_allrouters_linklocal(ip6addr) do{(ip6addr)->addr[0] = PP_HTONL(0xff020000UL); \
+ (ip6addr)->addr[1] = 0; \
+ (ip6addr)->addr[2] = 0; \
+ (ip6addr)->addr[3] = PP_HTONL(0x00000002UL);}while(0)
+
+#define ip6_addr_issolicitednode(ip6addr) ( ((ip6addr)->addr[0] == PP_HTONL(0xff020000UL)) && \
+ ((ip6addr)->addr[2] == PP_HTONL(0x00000001UL)) && \
+ (((ip6addr)->addr[3] & PP_HTONL(0xff000000UL)) == PP_HTONL(0xff000000UL)) )
+
+#define ip6_addr_set_solicitednode(ip6addr, if_id) do{(ip6addr)->addr[0] = PP_HTONL(0xff020000UL); \
+ (ip6addr)->addr[1] = 0; \
+ (ip6addr)->addr[2] = PP_HTONL(0x00000001UL); \
+ (ip6addr)->addr[3] = (PP_HTONL(0xff000000UL) | (if_id));}while(0)
+
+#define ip6_addr_cmp_solicitednode(ip6addr, sn_addr) (((ip6addr)->addr[0] == PP_HTONL(0xff020000UL)) && \
+ ((ip6addr)->addr[1] == 0) && \
+ ((ip6addr)->addr[2] == PP_HTONL(0x00000001UL)) && \
+ ((ip6addr)->addr[3] == (PP_HTONL(0xff000000UL) | (sn_addr)->addr[3])))
+
+/* IPv6 address states. */
+#define IP6_ADDR_INVALID 0x00
+#define IP6_ADDR_TENTATIVE 0x08
+#define IP6_ADDR_TENTATIVE_1 0x09 /* 1 probe sent */
+#define IP6_ADDR_TENTATIVE_2 0x0a /* 2 probes sent */
+#define IP6_ADDR_TENTATIVE_3 0x0b /* 3 probes sent */
+#define IP6_ADDR_TENTATIVE_4 0x0c /* 4 probes sent */
+#define IP6_ADDR_TENTATIVE_5 0x0d /* 5 probes sent */
+#define IP6_ADDR_TENTATIVE_6 0x0e /* 6 probes sent */
+#define IP6_ADDR_TENTATIVE_7 0x0f /* 7 probes sent */
+#define IP6_ADDR_VALID 0x10 /* This bit marks an address as valid (preferred or deprecated) */
+#define IP6_ADDR_PREFERRED 0x30
+#define IP6_ADDR_DEPRECATED 0x10 /* Same as VALID (valid but not preferred) */
+
+#define IP6_ADDR_TENTATIVE_COUNT_MASK 0x07 /* 1-7 probes sent */
+
+#define ip6_addr_isinvalid(addr_state) (addr_state == IP6_ADDR_INVALID)
+#define ip6_addr_istentative(addr_state) (addr_state & IP6_ADDR_TENTATIVE)
+#define ip6_addr_isvalid(addr_state) (addr_state & IP6_ADDR_VALID) /* Include valid, preferred, and deprecated. */
+#define ip6_addr_ispreferred(addr_state) (addr_state == IP6_ADDR_PREFERRED)
+#define ip6_addr_isdeprecated(addr_state) (addr_state == IP6_ADDR_DEPRECATED)
+
+#define ip6_addr_debug_print_parts(debug, a, b, c, d, e, f, g, h) \
+ LWIP_DEBUGF(debug, ("%" X16_F ":%" X16_F ":%" X16_F ":%" X16_F ":%" X16_F ":%" X16_F ":%" X16_F ":%" X16_F, \
+ a, b, c, d, e, f, g, h))
+#define ip6_addr_debug_print(debug, ipaddr) \
+ ip6_addr_debug_print_parts(debug, \
+ (u16_t)((ipaddr) != NULL ? IP6_ADDR_BLOCK1(ipaddr) : 0), \
+ (u16_t)((ipaddr) != NULL ? IP6_ADDR_BLOCK2(ipaddr) : 0), \
+ (u16_t)((ipaddr) != NULL ? IP6_ADDR_BLOCK3(ipaddr) : 0), \
+ (u16_t)((ipaddr) != NULL ? IP6_ADDR_BLOCK4(ipaddr) : 0), \
+ (u16_t)((ipaddr) != NULL ? IP6_ADDR_BLOCK5(ipaddr) : 0), \
+ (u16_t)((ipaddr) != NULL ? IP6_ADDR_BLOCK6(ipaddr) : 0), \
+ (u16_t)((ipaddr) != NULL ? IP6_ADDR_BLOCK7(ipaddr) : 0), \
+ (u16_t)((ipaddr) != NULL ? IP6_ADDR_BLOCK8(ipaddr) : 0))
+#define ip6_addr_debug_print_val(debug, ipaddr) \
+ ip6_addr_debug_print_parts(debug, \
+ IP6_ADDR_BLOCK1(&(ipaddr)), \
+ IP6_ADDR_BLOCK2(&(ipaddr)), \
+ IP6_ADDR_BLOCK3(&(ipaddr)), \
+ IP6_ADDR_BLOCK4(&(ipaddr)), \
+ IP6_ADDR_BLOCK5(&(ipaddr)), \
+ IP6_ADDR_BLOCK6(&(ipaddr)), \
+ IP6_ADDR_BLOCK7(&(ipaddr)), \
+ IP6_ADDR_BLOCK8(&(ipaddr)))
+
+#define IP6ADDR_STRLEN_MAX 46
+
+int ip6addr_aton(const char *cp, ip6_addr_t *addr);
+/** returns ptr to static buffer; not reentrant! */
+char *ip6addr_ntoa(const ip6_addr_t *addr);
+char *ip6addr_ntoa_r(const ip6_addr_t *addr, char *buf, int buflen);
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_IPV6 */
+
+#endif /* LWIP_HDR_IP6_ADDR_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/ip6_frag.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/ip6_frag.h
new file mode 100644
index 0000000..6be2747
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/ip6_frag.h
@@ -0,0 +1,120 @@
+/**
+ * @file
+ *
+ * IPv6 fragmentation and reassembly.
+ */
+
+/*
+ * Copyright (c) 2010 Inico Technologies Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Ivan Delamer <delamer@inicotech.com>
+ *
+ *
+ * Please coordinate changes and requests with Ivan Delamer
+ * <delamer@inicotech.com>
+ */
+#ifndef LWIP_HDR_IP6_FRAG_H
+#define LWIP_HDR_IP6_FRAG_H
+
+#include "lwip/opt.h"
+#include "lwip/pbuf.h"
+#include "lwip/ip6_addr.h"
+#include "lwip/ip6.h"
+#include "lwip/netif.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#if LWIP_IPV6 && LWIP_IPV6_REASS /* don't build if not configured for use in lwipopts.h */
+
+/** IP6_FRAG_COPYHEADER==1: for platforms where sizeof(void*) > 4, this needs to
+ * be enabled (to not overwrite part of the data). When enabled, the IPv6 header
+ * is copied instead of referencing it, which gives more room for struct ip6_reass_helper */
+#ifndef IPV6_FRAG_COPYHEADER
+#define IPV6_FRAG_COPYHEADER 0
+#endif
+
+/** The IPv6 reassembly timer interval in milliseconds. */
+#define IP6_REASS_TMR_INTERVAL 1000
+
+/* Copy the complete header of the first fragment to struct ip6_reassdata
+ or just point to its original location in the first pbuf? */
+#if IPV6_FRAG_COPYHEADER
+#define IPV6_FRAG_HDRPTR
+#define IPV6_FRAG_HDRREF(hdr) (&(hdr))
+#else /* IPV6_FRAG_COPYHEADER */
+#define IPV6_FRAG_HDRPTR *
+#define IPV6_FRAG_HDRREF(hdr) (hdr)
+#endif /* IPV6_FRAG_COPYHEADER */
+
+/** IPv6 reassembly helper struct.
+ * This is exported because memp needs to know the size.
+ */
+struct ip6_reassdata {
+ struct ip6_reassdata *next;
+ struct pbuf *p;
+ struct ip6_hdr IPV6_FRAG_HDRPTR iphdr;
+ u32_t identification;
+ u16_t datagram_len;
+ u8_t nexth;
+ u8_t timer;
+};
+
+#define ip6_reass_init() /* Compatibility define */
+void ip6_reass_tmr(void);
+struct pbuf *ip6_reass(struct pbuf *p);
+
+#endif /* LWIP_IPV6 && LWIP_IPV6_REASS */
+
+#if LWIP_IPV6 && LWIP_IPV6_FRAG /* don't build if not configured for use in lwipopts.h */
+
+#ifndef LWIP_PBUF_CUSTOM_REF_DEFINED
+#define LWIP_PBUF_CUSTOM_REF_DEFINED
+/** A custom pbuf that holds a reference to another pbuf, which is freed
+ * when this custom pbuf is freed. This is used to create a custom PBUF_REF
+ * that points into the original pbuf. */
+struct pbuf_custom_ref {
+ /** 'base class' */
+ struct pbuf_custom pc;
+ /** pointer to the original pbuf that is referenced */
+ struct pbuf *original;
+};
+#endif /* LWIP_PBUF_CUSTOM_REF_DEFINED */
+
+err_t ip6_frag(struct pbuf *p, struct netif *netif, const ip6_addr_t *dest);
+
+#endif /* LWIP_IPV6 && LWIP_IPV6_FRAG */
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_IP6_FRAG_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/ip_addr.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/ip_addr.h
new file mode 100644
index 0000000..11f65d2
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/ip_addr.h
@@ -0,0 +1,407 @@
+/**
+ * @file
+ * IP address API (common IPv4 and IPv6)
+ */
+
+/*
+ * 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_HDR_IP_ADDR_H
+#define LWIP_HDR_IP_ADDR_H
+
+#include "lwip/opt.h"
+#include "lwip/def.h"
+
+#include "lwip/ip4_addr.h"
+#include "lwip/ip6_addr.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** @ingroup ipaddr
+ * IP address types for use in ip_addr_t.type member.
+ * @see tcp_new_ip_type(), udp_new_ip_type(), raw_new_ip_type().
+ */
+enum lwip_ip_addr_type {
+ /** IPv4 */
+ IPADDR_TYPE_V4 = 0U,
+ /** IPv6 */
+ IPADDR_TYPE_V6 = 6U,
+ /** IPv4+IPv6 ("dual-stack") */
+ IPADDR_TYPE_ANY = 46U
+};
+
+#if LWIP_IPV4 && LWIP_IPV6
+/**
+ * @ingroup ipaddr
+ * A union struct for both IP version's addresses.
+ * ATTENTION: watch out for its size when adding IPv6 address scope!
+ */
+typedef struct ip_addr {
+ union {
+ ip6_addr_t ip6;
+ ip4_addr_t ip4;
+ } u_addr;
+ /** @ref lwip_ip_addr_type */
+ u8_t type;
+} ip_addr_t;
+
+extern const ip_addr_t ip_addr_any_type;
+
+/** @ingroup ip4addr */
+#define IPADDR4_INIT(u32val) { { { { u32val, 0ul, 0ul, 0ul } } }, IPADDR_TYPE_V4 }
+/** @ingroup ip4addr */
+#define IPADDR4_INIT_BYTES(a,b,c,d) IPADDR4_INIT(PP_HTONL(LWIP_MAKEU32(a,b,c,d)))
+/** @ingroup ip6addr */
+#define IPADDR6_INIT(a, b, c, d) { { { { a, b, c, d } } }, IPADDR_TYPE_V6 }
+/** @ingroup ip6addr */
+#define IPADDR6_INIT_HOST(a, b, c, d) { { { { PP_HTONL(a), PP_HTONL(b), PP_HTONL(c), PP_HTONL(d) } } }, IPADDR_TYPE_V6 }
+
+/** @ingroup ipaddr */
+#define IP_IS_ANY_TYPE_VAL(ipaddr) (IP_GET_TYPE(&ipaddr) == IPADDR_TYPE_ANY)
+/** @ingroup ipaddr */
+#define IPADDR_ANY_TYPE_INIT { { { { 0ul, 0ul, 0ul, 0ul } } }, IPADDR_TYPE_ANY }
+
+/** @ingroup ip4addr */
+#define IP_IS_V4_VAL(ipaddr) (IP_GET_TYPE(&ipaddr) == IPADDR_TYPE_V4)
+/** @ingroup ip6addr */
+#define IP_IS_V6_VAL(ipaddr) (IP_GET_TYPE(&ipaddr) == IPADDR_TYPE_V6)
+/** @ingroup ip4addr */
+#define IP_IS_V4(ipaddr) (((ipaddr) == NULL) || IP_IS_V4_VAL(*(ipaddr)))
+/** @ingroup ip6addr */
+#define IP_IS_V6(ipaddr) (((ipaddr) != NULL) && IP_IS_V6_VAL(*(ipaddr)))
+
+#define IP_SET_TYPE_VAL(ipaddr, iptype) do { (ipaddr).type = (iptype); }while(0)
+#define IP_SET_TYPE(ipaddr, iptype) do { if((ipaddr) != NULL) { IP_SET_TYPE_VAL(*(ipaddr), iptype); }}while(0)
+#define IP_GET_TYPE(ipaddr) ((ipaddr)->type)
+
+#define IP_ADDR_PCB_VERSION_MATCH_EXACT(pcb, ipaddr) (IP_GET_TYPE(&pcb->local_ip) == IP_GET_TYPE(ipaddr))
+#define IP_ADDR_PCB_VERSION_MATCH(pcb, ipaddr) (IP_IS_ANY_TYPE_VAL(pcb->local_ip) || IP_ADDR_PCB_VERSION_MATCH_EXACT(pcb, ipaddr))
+
+/** @ingroup ip6addr
+ * Convert generic ip address to specific protocol version
+ */
+#define ip_2_ip6(ipaddr) (&((ipaddr)->u_addr.ip6))
+/** @ingroup ip4addr
+ * Convert generic ip address to specific protocol version
+ */
+#define ip_2_ip4(ipaddr) (&((ipaddr)->u_addr.ip4))
+
+/** @ingroup ip4addr */
+#define IP_ADDR4(ipaddr,a,b,c,d) do { IP4_ADDR(ip_2_ip4(ipaddr),a,b,c,d); \
+ IP_SET_TYPE_VAL(*(ipaddr), IPADDR_TYPE_V4); } while(0)
+/** @ingroup ip6addr */
+#define IP_ADDR6(ipaddr,i0,i1,i2,i3) do { IP6_ADDR(ip_2_ip6(ipaddr),i0,i1,i2,i3); \
+ IP_SET_TYPE_VAL(*(ipaddr), IPADDR_TYPE_V6); } while(0)
+/** @ingroup ip6addr */
+#define IP_ADDR6_HOST(ipaddr,i0,i1,i2,i3) IP_ADDR6(ipaddr,PP_HTONL(i0),PP_HTONL(i1),PP_HTONL(i2),PP_HTONL(i3))
+
+/** @ingroup ipaddr */
+#define ip_addr_copy(dest, src) do{ IP_SET_TYPE_VAL(dest, IP_GET_TYPE(&src)); if(IP_IS_V6_VAL(src)){ \
+ ip6_addr_copy(*ip_2_ip6(&(dest)), *ip_2_ip6(&(src))); }else{ \
+ ip4_addr_copy(*ip_2_ip4(&(dest)), *ip_2_ip4(&(src))); }}while(0)
+/** @ingroup ip6addr */
+#define ip_addr_copy_from_ip6(dest, src) do{ \
+ ip6_addr_copy(*ip_2_ip6(&(dest)), src); IP_SET_TYPE_VAL(dest, IPADDR_TYPE_V6); }while(0)
+/** @ingroup ip4addr */
+#define ip_addr_copy_from_ip4(dest, src) do{ \
+ ip4_addr_copy(*ip_2_ip4(&(dest)), src); IP_SET_TYPE_VAL(dest, IPADDR_TYPE_V4); }while(0)
+/** @ingroup ip4addr */
+#define ip_addr_set_ip4_u32(ipaddr, val) do{if(ipaddr){ip4_addr_set_u32(ip_2_ip4(ipaddr), val); \
+ IP_SET_TYPE(ipaddr, IPADDR_TYPE_V4); }}while(0)
+/** @ingroup ip4addr */
+#define ip_addr_get_ip4_u32(ipaddr) (((ipaddr) && IP_IS_V4(ipaddr)) ? \
+ ip4_addr_get_u32(ip_2_ip4(ipaddr)) : 0)
+/** @ingroup ipaddr */
+#define ip_addr_set(dest, src) do{ IP_SET_TYPE(dest, IP_GET_TYPE(src)); if(IP_IS_V6(src)){ \
+ ip6_addr_set(ip_2_ip6(dest), ip_2_ip6(src)); }else{ \
+ ip4_addr_set(ip_2_ip4(dest), ip_2_ip4(src)); }}while(0)
+/** @ingroup ipaddr */
+#define ip_addr_set_ipaddr(dest, src) ip_addr_set(dest, src)
+/** @ingroup ipaddr */
+#define ip_addr_set_zero(ipaddr) do{ \
+ ip6_addr_set_zero(ip_2_ip6(ipaddr)); IP_SET_TYPE(ipaddr, 0); }while(0)
+/** @ingroup ip5addr */
+#define ip_addr_set_zero_ip4(ipaddr) do{ \
+ ip6_addr_set_zero(ip_2_ip6(ipaddr)); IP_SET_TYPE(ipaddr, IPADDR_TYPE_V4); }while(0)
+/** @ingroup ip6addr */
+#define ip_addr_set_zero_ip6(ipaddr) do{ \
+ ip6_addr_set_zero(ip_2_ip6(ipaddr)); IP_SET_TYPE(ipaddr, IPADDR_TYPE_V6); }while(0)
+/** @ingroup ipaddr */
+#define ip_addr_set_any(is_ipv6, ipaddr) do{if(is_ipv6){ \
+ ip6_addr_set_any(ip_2_ip6(ipaddr)); IP_SET_TYPE(ipaddr, IPADDR_TYPE_V6); }else{ \
+ ip4_addr_set_any(ip_2_ip4(ipaddr)); IP_SET_TYPE(ipaddr, IPADDR_TYPE_V4); }}while(0)
+/** @ingroup ipaddr */
+#define ip_addr_set_loopback(is_ipv6, ipaddr) do{if(is_ipv6){ \
+ ip6_addr_set_loopback(ip_2_ip6(ipaddr)); IP_SET_TYPE(ipaddr, IPADDR_TYPE_V6); }else{ \
+ ip4_addr_set_loopback(ip_2_ip4(ipaddr)); IP_SET_TYPE(ipaddr, IPADDR_TYPE_V4); }}while(0)
+/** @ingroup ipaddr */
+#define ip_addr_set_hton(dest, src) do{if(IP_IS_V6(src)){ \
+ ip6_addr_set_hton(ip_2_ip6(ipaddr), (src)); IP_SET_TYPE(dest, IPADDR_TYPE_V6); }else{ \
+ ip4_addr_set_hton(ip_2_ip4(ipaddr), (src)); IP_SET_TYPE(dest, IPADDR_TYPE_V4); }}while(0)
+/** @ingroup ipaddr */
+#define ip_addr_get_network(target, host, netmask) do{if(IP_IS_V6(host)){ \
+ ip4_addr_set_zero(ip_2_ip4(target)); IP_SET_TYPE(target, IPADDR_TYPE_V6); } else { \
+ ip4_addr_get_network(ip_2_ip4(target), ip_2_ip4(host), ip_2_ip4(netmask)); IP_SET_TYPE(target, IPADDR_TYPE_V4); }}while(0)
+/** @ingroup ipaddr */
+#define ip_addr_netcmp(addr1, addr2, mask) ((IP_IS_V6(addr1) && IP_IS_V6(addr2)) ? \
+ 0 : \
+ ip4_addr_netcmp(ip_2_ip4(addr1), ip_2_ip4(addr2), mask))
+/** @ingroup ipaddr */
+#define ip_addr_cmp(addr1, addr2) ((IP_GET_TYPE(addr1) != IP_GET_TYPE(addr2)) ? 0 : (IP_IS_V6_VAL(*(addr1)) ? \
+ ip6_addr_cmp(ip_2_ip6(addr1), ip_2_ip6(addr2)) : \
+ ip4_addr_cmp(ip_2_ip4(addr1), ip_2_ip4(addr2))))
+/** @ingroup ipaddr */
+#define ip_addr_isany(ipaddr) ((IP_IS_V6(ipaddr)) ? \
+ ip6_addr_isany(ip_2_ip6(ipaddr)) : \
+ ip4_addr_isany(ip_2_ip4(ipaddr)))
+/** @ingroup ipaddr */
+#define ip_addr_isany_val(ipaddr) ((IP_IS_V6_VAL(ipaddr)) ? \
+ ip6_addr_isany_val(*ip_2_ip6(&(ipaddr))) : \
+ ip4_addr_isany_val(*ip_2_ip4(&(ipaddr))))
+/** @ingroup ipaddr */
+#define ip_addr_isbroadcast(ipaddr, netif) ((IP_IS_V6(ipaddr)) ? \
+ 0 : \
+ ip4_addr_isbroadcast(ip_2_ip4(ipaddr), netif))
+/** @ingroup ipaddr */
+#define ip_addr_ismulticast(ipaddr) ((IP_IS_V6(ipaddr)) ? \
+ ip6_addr_ismulticast(ip_2_ip6(ipaddr)) : \
+ ip4_addr_ismulticast(ip_2_ip4(ipaddr)))
+/** @ingroup ipaddr */
+#define ip_addr_isloopback(ipaddr) ((IP_IS_V6(ipaddr)) ? \
+ ip6_addr_isloopback(ip_2_ip6(ipaddr)) : \
+ ip4_addr_isloopback(ip_2_ip4(ipaddr)))
+/** @ingroup ipaddr */
+#define ip_addr_islinklocal(ipaddr) ((IP_IS_V6(ipaddr)) ? \
+ ip6_addr_islinklocal(ip_2_ip6(ipaddr)) : \
+ ip4_addr_islinklocal(ip_2_ip4(ipaddr)))
+#define ip_addr_debug_print(debug, ipaddr) do { if(IP_IS_V6(ipaddr)) { \
+ ip6_addr_debug_print(debug, ip_2_ip6(ipaddr)); } else { \
+ ip4_addr_debug_print(debug, ip_2_ip4(ipaddr)); }}while(0)
+#define ip_addr_debug_print_val(debug, ipaddr) do { if(IP_IS_V6_VAL(ipaddr)) { \
+ ip6_addr_debug_print_val(debug, *ip_2_ip6(&(ipaddr))); } else { \
+ ip4_addr_debug_print_val(debug, *ip_2_ip4(&(ipaddr))); }}while(0)
+/** @ingroup ipaddr */
+#define ipaddr_ntoa(addr) (((addr) == NULL) ? "NULL" : \
+ ((IP_IS_V6(addr)) ? ip6addr_ntoa(ip_2_ip6(addr)) : ip4addr_ntoa(ip_2_ip4(addr))))
+/** @ingroup ipaddr */
+#define ipaddr_ntoa_r(addr, buf, buflen) (((addr) == NULL) ? "NULL" : \
+ ((IP_IS_V6(addr)) ? ip6addr_ntoa_r(ip_2_ip6(addr), buf, buflen) : ip4addr_ntoa_r(ip_2_ip4(addr), buf, buflen)))
+int ipaddr_aton(const char *cp, ip_addr_t *addr);
+
+/** @ingroup ipaddr */
+#define IPADDR_STRLEN_MAX IP6ADDR_STRLEN_MAX
+
+/** @ingroup ipaddr */
+#define ip4_2_ipv4_mapped_ipv6(ip6addr, ip4addr) do { \
+ (ip6addr)->addr[3] = (ip4addr)->addr; \
+ (ip6addr)->addr[2] = PP_HTONL(0x0000FFFFUL); \
+ (ip6addr)->addr[1] = 0; \
+ (ip6addr)->addr[0] = 0; } while(0);
+
+/** @ingroup ipaddr */
+#define unmap_ipv4_mapped_ipv6(ip4addr, ip6addr) \
+ (ip4addr)->addr = (ip6addr)->addr[3];
+
+#define IP46_ADDR_ANY(type) (((type) == IPADDR_TYPE_V6)? IP6_ADDR_ANY : IP4_ADDR_ANY)
+
+#else /* LWIP_IPV4 && LWIP_IPV6 */
+
+#define IP_ADDR_PCB_VERSION_MATCH(addr, pcb) 1
+#define IP_ADDR_PCB_VERSION_MATCH_EXACT(pcb, ipaddr) 1
+
+#if LWIP_IPV4
+
+typedef ip4_addr_t ip_addr_t;
+#define IPADDR4_INIT(u32val) { u32val }
+#define IPADDR4_INIT_BYTES(a,b,c,d) IPADDR4_INIT(PP_HTONL(LWIP_MAKEU32(a,b,c,d)))
+#define IP_IS_V4_VAL(ipaddr) 1
+#define IP_IS_V6_VAL(ipaddr) 0
+#define IP_IS_V4(ipaddr) 1
+#define IP_IS_V6(ipaddr) 0
+#define IP_IS_ANY_TYPE_VAL(ipaddr) 0
+#define IP_SET_TYPE_VAL(ipaddr, iptype)
+#define IP_SET_TYPE(ipaddr, iptype)
+#define IP_GET_TYPE(ipaddr) IPADDR_TYPE_V4
+#define ip_2_ip4(ipaddr) (ipaddr)
+#define IP_ADDR4(ipaddr,a,b,c,d) IP4_ADDR(ipaddr,a,b,c,d)
+
+#define ip_addr_copy(dest, src) ip4_addr_copy(dest, src)
+#define ip_addr_copy_from_ip4(dest, src) ip4_addr_copy(dest, src)
+#define ip_addr_set_ip4_u32(ipaddr, val) ip4_addr_set_u32(ip_2_ip4(ipaddr), val)
+#define ip_addr_get_ip4_u32(ipaddr) ip4_addr_get_u32(ip_2_ip4(ipaddr))
+#define ip_addr_set(dest, src) ip4_addr_set(dest, src)
+#define ip_addr_set_ipaddr(dest, src) ip4_addr_set(dest, src)
+#define ip_addr_set_zero(ipaddr) ip4_addr_set_zero(ipaddr)
+#define ip_addr_set_zero_ip4(ipaddr) ip4_addr_set_zero(ipaddr)
+#define ip_addr_set_any(is_ipv6, ipaddr) ip4_addr_set_any(ipaddr)
+#define ip_addr_set_loopback(is_ipv6, ipaddr) ip4_addr_set_loopback(ipaddr)
+#define ip_addr_set_hton(dest, src) ip4_addr_set_hton(dest, src)
+#define ip_addr_get_network(target, host, mask) ip4_addr_get_network(target, host, mask)
+#define ip_addr_netcmp(addr1, addr2, mask) ip4_addr_netcmp(addr1, addr2, mask)
+#define ip_addr_cmp(addr1, addr2) ip4_addr_cmp(addr1, addr2)
+#define ip_addr_isany(ipaddr) ip4_addr_isany(ipaddr)
+#define ip_addr_isany_val(ipaddr) ip4_addr_isany_val(ipaddr)
+#define ip_addr_isloopback(ipaddr) ip4_addr_isloopback(ipaddr)
+#define ip_addr_islinklocal(ipaddr) ip4_addr_islinklocal(ipaddr)
+#define ip_addr_isbroadcast(addr, netif) ip4_addr_isbroadcast(addr, netif)
+#define ip_addr_ismulticast(ipaddr) ip4_addr_ismulticast(ipaddr)
+#define ip_addr_debug_print(debug, ipaddr) ip4_addr_debug_print(debug, ipaddr)
+#define ip_addr_debug_print_val(debug, ipaddr) ip4_addr_debug_print_val(debug, ipaddr)
+#define ipaddr_ntoa(ipaddr) ip4addr_ntoa(ipaddr)
+#define ipaddr_ntoa_r(ipaddr, buf, buflen) ip4addr_ntoa_r(ipaddr, buf, buflen)
+#define ipaddr_aton(cp, addr) ip4addr_aton(cp, addr)
+
+#define IPADDR_STRLEN_MAX IP4ADDR_STRLEN_MAX
+
+#define IP46_ADDR_ANY(type) (IP4_ADDR_ANY)
+
+#else /* LWIP_IPV4 */
+
+typedef ip6_addr_t ip_addr_t;
+#define IPADDR6_INIT(a, b, c, d) { { a, b, c, d } }
+#define IPADDR6_INIT_HOST(a, b, c, d) { { PP_HTONL(a), PP_HTONL(b), PP_HTONL(c), PP_HTONL(d) } }
+#define IP_IS_V4_VAL(ipaddr) 0
+#define IP_IS_V6_VAL(ipaddr) 1
+#define IP_IS_V4(ipaddr) 0
+#define IP_IS_V6(ipaddr) 1
+#define IP_IS_ANY_TYPE_VAL(ipaddr) 0
+#define IP_SET_TYPE_VAL(ipaddr, iptype)
+#define IP_SET_TYPE(ipaddr, iptype)
+#define IP_GET_TYPE(ipaddr) IPADDR_TYPE_V6
+#define ip_2_ip6(ipaddr) (ipaddr)
+#define IP_ADDR6(ipaddr,i0,i1,i2,i3) IP6_ADDR(ipaddr,i0,i1,i2,i3)
+#define IP_ADDR6_HOST(ipaddr,i0,i1,i2,i3) IP_ADDR6(ipaddr,PP_HTONL(i0),PP_HTONL(i1),PP_HTONL(i2),PP_HTONL(i3))
+
+#define ip_addr_copy(dest, src) ip6_addr_copy(dest, src)
+#define ip_addr_copy_from_ip6(dest, src) ip6_addr_copy(dest, src)
+#define ip_addr_set(dest, src) ip6_addr_set(dest, src)
+#define ip_addr_set_ipaddr(dest, src) ip6_addr_set(dest, src)
+#define ip_addr_set_zero(ipaddr) ip6_addr_set_zero(ipaddr)
+#define ip_addr_set_zero_ip6(ipaddr) ip6_addr_set_zero(ipaddr)
+#define ip_addr_set_any(is_ipv6, ipaddr) ip6_addr_set_any(ipaddr)
+#define ip_addr_set_loopback(is_ipv6, ipaddr) ip6_addr_set_loopback(ipaddr)
+#define ip_addr_set_hton(dest, src) ip6_addr_set_hton(dest, src)
+#define ip_addr_get_network(target, host, mask) ip6_addr_set_zero(target)
+#define ip_addr_netcmp(addr1, addr2, mask) 0
+#define ip_addr_cmp(addr1, addr2) ip6_addr_cmp(addr1, addr2)
+#define ip_addr_isany(ipaddr) ip6_addr_isany(ipaddr)
+#define ip_addr_isany_val(ipaddr) ip6_addr_isany_val(ipaddr)
+#define ip_addr_isloopback(ipaddr) ip6_addr_isloopback(ipaddr)
+#define ip_addr_islinklocal(ipaddr) ip6_addr_islinklocal(ipaddr)
+#define ip_addr_isbroadcast(addr, netif) 0
+#define ip_addr_ismulticast(ipaddr) ip6_addr_ismulticast(ipaddr)
+#define ip_addr_debug_print(debug, ipaddr) ip6_addr_debug_print(debug, ipaddr)
+#define ip_addr_debug_print_val(debug, ipaddr) ip6_addr_debug_print_val(debug, ipaddr)
+#define ipaddr_ntoa(ipaddr) ip6addr_ntoa(ipaddr)
+#define ipaddr_ntoa_r(ipaddr, buf, buflen) ip6addr_ntoa_r(ipaddr, buf, buflen)
+#define ipaddr_aton(cp, addr) ip6addr_aton(cp, addr)
+
+#define IPADDR_STRLEN_MAX IP6ADDR_STRLEN_MAX
+
+#define IP46_ADDR_ANY(type) (IP6_ADDR_ANY)
+
+#endif /* LWIP_IPV4 */
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+
+#if LWIP_IPV4
+
+extern const ip_addr_t ip_addr_any;
+extern const ip_addr_t ip_addr_broadcast;
+
+/**
+ * @ingroup ip4addr
+ * Can be used as a fixed/const ip_addr_t
+ * for the IP wildcard.
+ * Defined to @ref IP4_ADDR_ANY when IPv4 is enabled.
+ * Defined to @ref IP6_ADDR_ANY in IPv6 only systems.
+ * Use this if you can handle IPv4 _AND_ IPv6 addresses.
+ * Use @ref IP4_ADDR_ANY or @ref IP6_ADDR_ANY when the IP
+ * type matters.
+ */
+#define IP_ADDR_ANY IP4_ADDR_ANY
+/**
+ * @ingroup ip4addr
+ * Can be used as a fixed/const ip_addr_t
+ * for the IPv4 wildcard and the broadcast address
+ */
+#define IP4_ADDR_ANY (&ip_addr_any)
+/**
+ * @ingroup ip4addr
+ * Can be used as a fixed/const ip4_addr_t
+ * for the wildcard and the broadcast address
+ */
+#define IP4_ADDR_ANY4 (ip_2_ip4(&ip_addr_any))
+
+/** @ingroup ip4addr */
+#define IP_ADDR_BROADCAST (&ip_addr_broadcast)
+/** @ingroup ip4addr */
+#define IP4_ADDR_BROADCAST (ip_2_ip4(&ip_addr_broadcast))
+
+#endif /* LWIP_IPV4*/
+
+#if LWIP_IPV6
+
+extern const ip_addr_t ip6_addr_any;
+
+/**
+ * @ingroup ip6addr
+ * IP6_ADDR_ANY can be used as a fixed ip_addr_t
+ * for the IPv6 wildcard address
+ */
+#define IP6_ADDR_ANY (&ip6_addr_any)
+/**
+ * @ingroup ip6addr
+ * IP6_ADDR_ANY6 can be used as a fixed ip6_addr_t
+ * for the IPv6 wildcard address
+ */
+#define IP6_ADDR_ANY6 (ip_2_ip6(&ip6_addr_any))
+
+#if !LWIP_IPV4
+/** IPv6-only configurations */
+#define IP_ADDR_ANY IP6_ADDR_ANY
+#endif /* !LWIP_IPV4 */
+
+#endif
+
+#if LWIP_IPV4 && LWIP_IPV6
+/** @ingroup ipaddr */
+#define IP_ANY_TYPE (&ip_addr_any_type)
+#else
+#define IP_ANY_TYPE IP_ADDR_ANY
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_IP_ADDR_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/ip_frag.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/ip_frag.h
new file mode 100644
index 0000000..5e7227e
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/ip_frag.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Jani Monoses <jani@iv.ro>
+ *
+ */
+
+#ifndef LWIP_HDR_IP_FRAG_H
+#define LWIP_HDR_IP_FRAG_H
+
+#include "lwip/opt.h"
+#include "lwip/err.h"
+#include "lwip/pbuf.h"
+#include "lwip/netif.h"
+#include "lwip/ip_addr.h"
+#include "lwip/ip.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if IP_REASSEMBLY
+/* The IP reassembly timer interval in milliseconds. */
+#define IP_TMR_INTERVAL 1000
+
+/* IP reassembly helper struct.
+ * This is exported because memp needs to know the size.
+ */
+struct ip_reassdata {
+ struct ip_reassdata *next;
+ struct pbuf *p;
+ struct ip_hdr iphdr;
+ u16_t datagram_len;
+ u8_t flags;
+ u8_t timer;
+};
+
+void ip_reass_init(void);
+void ip_reass_tmr(void);
+struct pbuf * ip_reass(struct pbuf *p);
+#endif /* IP_REASSEMBLY */
+
+#if IP_FRAG
+#if !IP_FRAG_USES_STATIC_BUF && !LWIP_NETIF_TX_SINGLE_PBUF
+/** A custom pbuf that holds a reference to another pbuf, which is freed
+ * when this custom pbuf is freed. This is used to create a custom PBUF_REF
+ * that points into the original pbuf. */
+#ifndef LWIP_PBUF_CUSTOM_REF_DEFINED
+#define LWIP_PBUF_CUSTOM_REF_DEFINED
+struct pbuf_custom_ref {
+ /** 'base class' */
+ struct pbuf_custom pc;
+ /** pointer to the original pbuf that is referenced */
+ struct pbuf *original;
+};
+#endif /* LWIP_PBUF_CUSTOM_REF_DEFINED */
+#endif /* !IP_FRAG_USES_STATIC_BUF && !LWIP_NETIF_TX_SINGLE_PBUF */
+
+err_t ip_frag(struct pbuf *p, struct netif *netif, const ip_addr_t *dest);
+#endif /* IP_FRAG */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_IP_FRAG_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/mem.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/mem.h
new file mode 100644
index 0000000..ff208d2
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/mem.h
@@ -0,0 +1,82 @@
+/**
+ * @file
+ * Heap API
+ */
+
+/*
+ * 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_HDR_MEM_H
+#define LWIP_HDR_MEM_H
+
+#include "lwip/opt.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if MEM_LIBC_MALLOC
+
+#include "lwip/arch.h"
+
+typedef size_t mem_size_t;
+#define MEM_SIZE_F SZT_F
+
+#elif MEM_USE_POOLS
+
+typedef u16_t mem_size_t;
+#define MEM_SIZE_F U16_F
+
+#else
+
+/* MEM_SIZE would have to be aligned, but using 64000 here instead of
+ * 65535 leaves some room for alignment...
+ */
+#if MEM_SIZE > 64000L
+typedef u32_t mem_size_t;
+#define MEM_SIZE_F U32_F
+#else
+typedef u16_t mem_size_t;
+#define MEM_SIZE_F U16_F
+#endif /* MEM_SIZE > 64000 */
+#endif
+
+void mem_init(void);
+void *mem_trim(void *mem, mem_size_t size);
+void *mem_malloc(mem_size_t size);
+void *mem_calloc(mem_size_t count, mem_size_t size);
+void mem_free(void *mem);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_MEM_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/memp.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/memp.h
new file mode 100644
index 0000000..68fcd99
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/memp.h
@@ -0,0 +1,153 @@
+/**
+ * @file
+ * Memory pool API
+ */
+
+/*
+ * 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_HDR_MEMP_H
+#define LWIP_HDR_MEMP_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* run once with empty definition to handle all custom includes in lwippools.h */
+#define LWIP_MEMPOOL(name,num,size,desc)
+#include "lwip/priv/memp_std.h"
+
+/** Create the list of all memory pools managed by memp. MEMP_MAX represents a NULL pool at the end */
+typedef enum {
+#define LWIP_MEMPOOL(name,num,size,desc) MEMP_##name,
+#include "lwip/priv/memp_std.h"
+ MEMP_MAX
+} memp_t;
+
+#include "lwip/priv/memp_priv.h"
+#include "lwip/stats.h"
+
+extern const struct memp_desc* const memp_pools[MEMP_MAX];
+
+/**
+ * @ingroup mempool
+ * Declare prototype for private memory pool if it is used in multiple files
+ */
+#define LWIP_MEMPOOL_PROTOTYPE(name) extern const struct memp_desc memp_ ## name
+
+#if MEMP_MEM_MALLOC
+
+#define LWIP_MEMPOOL_DECLARE(name,num,size,desc) \
+ LWIP_MEMPOOL_DECLARE_STATS_INSTANCE(memp_stats_ ## name) \
+ const struct memp_desc memp_ ## name = { \
+ DECLARE_LWIP_MEMPOOL_DESC(desc) \
+ LWIP_MEMPOOL_DECLARE_STATS_REFERENCE(memp_stats_ ## name) \
+ LWIP_MEM_ALIGN_SIZE(size) \
+ };
+
+#else /* MEMP_MEM_MALLOC */
+
+/**
+ * @ingroup mempool
+ * Declare a private memory pool
+ * Private mempools example:
+ * .h: only when pool is used in multiple .c files: LWIP_MEMPOOL_PROTOTYPE(my_private_pool);
+ * .c:
+ * - in global variables section: LWIP_MEMPOOL_DECLARE(my_private_pool, 10, sizeof(foo), "Some description")
+ * - call ONCE before using pool (e.g. in some init() function): LWIP_MEMPOOL_INIT(my_private_pool);
+ * - allocate: void* my_new_mem = LWIP_MEMPOOL_ALLOC(my_private_pool);
+ * - free: LWIP_MEMPOOL_FREE(my_private_pool, my_new_mem);
+ *
+ * To relocate a pool, declare it as extern in cc.h. Example for GCC:
+ * extern u8_t __attribute__((section(".onchip_mem"))) memp_memory_my_private_pool[];
+ */
+#define LWIP_MEMPOOL_DECLARE(name,num,size,desc) \
+ LWIP_DECLARE_MEMORY_ALIGNED(memp_memory_ ## name ## _base, ((num) * (MEMP_SIZE + MEMP_ALIGN_SIZE(size)))); \
+ \
+ LWIP_MEMPOOL_DECLARE_STATS_INSTANCE(memp_stats_ ## name) \
+ \
+ static struct memp *memp_tab_ ## name; \
+ \
+ const struct memp_desc memp_ ## name = { \
+ DECLARE_LWIP_MEMPOOL_DESC(desc) \
+ LWIP_MEMPOOL_DECLARE_STATS_REFERENCE(memp_stats_ ## name) \
+ LWIP_MEM_ALIGN_SIZE(size), \
+ (num), \
+ memp_memory_ ## name ## _base, \
+ &memp_tab_ ## name \
+ };
+
+#endif /* MEMP_MEM_MALLOC */
+
+/**
+ * @ingroup mempool
+ * Initialize a private memory pool
+ */
+#define LWIP_MEMPOOL_INIT(name) memp_init_pool(&memp_ ## name)
+/**
+ * @ingroup mempool
+ * Allocate from a private memory pool
+ */
+#define LWIP_MEMPOOL_ALLOC(name) memp_malloc_pool(&memp_ ## name)
+/**
+ * @ingroup mempool
+ * Free element from a private memory pool
+ */
+#define LWIP_MEMPOOL_FREE(name, x) memp_free_pool(&memp_ ## name, (x))
+
+#if MEM_USE_POOLS
+/** This structure is used to save the pool one element came from.
+ * This has to be defined here as it is required for pool size calculation. */
+struct memp_malloc_helper
+{
+ memp_t poolnr;
+#if MEMP_OVERFLOW_CHECK || (LWIP_STATS && MEM_STATS)
+ u16_t size;
+#endif /* MEMP_OVERFLOW_CHECK || (LWIP_STATS && MEM_STATS) */
+};
+#endif /* MEM_USE_POOLS */
+
+void memp_init(void);
+
+#if MEMP_OVERFLOW_CHECK
+void *memp_malloc_fn(memp_t type, const char* file, const int line);
+#define memp_malloc(t) memp_malloc_fn((t), __FILE__, __LINE__)
+#else
+void *memp_malloc(memp_t type);
+#endif
+void memp_free(memp_t type, void *mem);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_MEMP_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/memp_std.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/memp_std.h
new file mode 100644
index 0000000..05977e6
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/memp_std.h
@@ -0,0 +1,157 @@
+/*
+ * SETUP: Make sure we define everything we will need.
+ *
+ * We have create three types of pools:
+ * 1) MEMPOOL - standard pools
+ * 2) MALLOC_MEMPOOL - to be used by mem_malloc in mem.c
+ * 3) PBUF_MEMPOOL - a mempool of pbuf's, so include space for the pbuf struct
+ *
+ * If the include'r doesn't require any special treatment of each of the types
+ * above, then will declare #2 & #3 to be just standard mempools.
+ */
+#ifndef LWIP_MALLOC_MEMPOOL
+/* This treats "malloc pools" just like any other pool.
+ The pools are a little bigger to provide 'size' as the amount of user data. */
+#define LWIP_MALLOC_MEMPOOL(num, size) LWIP_MEMPOOL(POOL_##size, num, (size + LWIP_MEM_ALIGN_SIZE(sizeof(struct memp_malloc_helper))), "MALLOC_"#size)
+#define LWIP_MALLOC_MEMPOOL_START
+#define LWIP_MALLOC_MEMPOOL_END
+#endif /* LWIP_MALLOC_MEMPOOL */
+
+#ifndef LWIP_PBUF_MEMPOOL
+/* This treats "pbuf pools" just like any other pool.
+ * Allocates buffers for a pbuf struct AND a payload size */
+#define LWIP_PBUF_MEMPOOL(name, num, payload, desc) LWIP_MEMPOOL(name, num, (MEMP_ALIGN_SIZE(sizeof(struct pbuf)) + MEMP_ALIGN_SIZE(payload)), desc)
+#endif /* LWIP_PBUF_MEMPOOL */
+
+
+/*
+ * A list of internal pools used by LWIP.
+ *
+ * LWIP_MEMPOOL(pool_name, number_elements, element_size, pool_description)
+ * creates a pool name MEMP_pool_name. description is used in stats.c
+ */
+#if LWIP_RAW
+LWIP_MEMPOOL(RAW_PCB, MEMP_NUM_RAW_PCB, sizeof(struct raw_pcb), "RAW_PCB")
+#endif /* LWIP_RAW */
+
+#if LWIP_UDP
+LWIP_MEMPOOL(UDP_PCB, MEMP_NUM_UDP_PCB, sizeof(struct udp_pcb), "UDP_PCB")
+#endif /* LWIP_UDP */
+
+#if LWIP_TCP
+LWIP_MEMPOOL(TCP_PCB, MEMP_NUM_TCP_PCB, sizeof(struct tcp_pcb), "TCP_PCB")
+LWIP_MEMPOOL(TCP_PCB_LISTEN, MEMP_NUM_TCP_PCB_LISTEN, sizeof(struct tcp_pcb_listen), "TCP_PCB_LISTEN")
+LWIP_MEMPOOL(TCP_SEG, MEMP_NUM_TCP_SEG, sizeof(struct tcp_seg), "TCP_SEG")
+#endif /* LWIP_TCP */
+
+#if IP_REASSEMBLY
+LWIP_MEMPOOL(REASSDATA, MEMP_NUM_REASSDATA, sizeof(struct ip_reassdata), "REASSDATA")
+#endif /* IP_REASSEMBLY */
+#if (IP_FRAG && !IP_FRAG_USES_STATIC_BUF && !LWIP_NETIF_TX_SINGLE_PBUF) || (LWIP_IPV6 && LWIP_IPV6_FRAG)
+LWIP_MEMPOOL(FRAG_PBUF, MEMP_NUM_FRAG_PBUF, sizeof(struct pbuf_custom_ref),"FRAG_PBUF")
+#endif /* IP_FRAG && !IP_FRAG_USES_STATIC_BUF && !LWIP_NETIF_TX_SINGLE_PBUF */
+
+#if LWIP_NETCONN || LWIP_SOCKET
+LWIP_MEMPOOL(NETBUF, MEMP_NUM_NETBUF, sizeof(struct netbuf), "NETBUF")
+LWIP_MEMPOOL(NETCONN, MEMP_NUM_NETCONN, sizeof(struct netconn), "NETCONN")
+#endif /* LWIP_NETCONN || LWIP_SOCKET */
+
+#if NO_SYS==0
+LWIP_MEMPOOL(TCPIP_MSG_API, MEMP_NUM_TCPIP_MSG_API, sizeof(struct tcpip_msg), "TCPIP_MSG_API")
+#if LWIP_MPU_COMPATIBLE
+LWIP_MEMPOOL(API_MSG, MEMP_NUM_API_MSG, sizeof(struct api_msg), "API_MSG")
+#if LWIP_DNS
+LWIP_MEMPOOL(DNS_API_MSG, MEMP_NUM_DNS_API_MSG, sizeof(struct dns_api_msg), "DNS_API_MSG")
+#endif
+#if LWIP_SOCKET && !LWIP_TCPIP_CORE_LOCKING
+LWIP_MEMPOOL(SOCKET_SETGETSOCKOPT_DATA, MEMP_NUM_SOCKET_SETGETSOCKOPT_DATA, sizeof(struct lwip_setgetsockopt_data), "SOCKET_SETGETSOCKOPT_DATA")
+#endif
+#if LWIP_NETIF_API
+LWIP_MEMPOOL(NETIFAPI_MSG, MEMP_NUM_NETIFAPI_MSG, sizeof(struct netifapi_msg), "NETIFAPI_MSG")
+#endif
+#endif /* LWIP_MPU_COMPATIBLE */
+#if !LWIP_TCPIP_CORE_LOCKING_INPUT
+LWIP_MEMPOOL(TCPIP_MSG_INPKT,MEMP_NUM_TCPIP_MSG_INPKT, sizeof(struct tcpip_msg), "TCPIP_MSG_INPKT")
+#endif /* !LWIP_TCPIP_CORE_LOCKING_INPUT */
+#endif /* NO_SYS==0 */
+
+#if LWIP_ARP && ARP_QUEUEING
+LWIP_MEMPOOL(ARP_QUEUE, MEMP_NUM_ARP_QUEUE, sizeof(struct etharp_q_entry), "ARP_QUEUE")
+#endif /* LWIP_ARP && ARP_QUEUEING */
+
+#if LWIP_IGMP
+LWIP_MEMPOOL(IGMP_GROUP, MEMP_NUM_IGMP_GROUP, sizeof(struct igmp_group), "IGMP_GROUP")
+#endif /* LWIP_IGMP */
+
+#if (!NO_SYS || (NO_SYS && !NO_SYS_NO_TIMERS)) /* LWIP_TIMERS */
+LWIP_MEMPOOL(SYS_TIMEOUT, MEMP_NUM_SYS_TIMEOUT, sizeof(struct sys_timeo), "SYS_TIMEOUT")
+#endif /* LWIP_TIMERS */
+
+#if LWIP_SNMP
+LWIP_MEMPOOL(SNMP_ROOTNODE, MEMP_NUM_SNMP_ROOTNODE, sizeof(struct mib_list_rootnode), "SNMP_ROOTNODE")
+LWIP_MEMPOOL(SNMP_NODE, MEMP_NUM_SNMP_NODE, sizeof(struct mib_list_node), "SNMP_NODE")
+LWIP_MEMPOOL(SNMP_VARBIND, MEMP_NUM_SNMP_VARBIND, sizeof(struct snmp_varbind), "SNMP_VARBIND")
+LWIP_MEMPOOL(SNMP_VALUE, MEMP_NUM_SNMP_VALUE, SNMP_MAX_VALUE_SIZE, "SNMP_VALUE")
+#endif /* LWIP_SNMP */
+#if LWIP_DNS && LWIP_SOCKET
+LWIP_MEMPOOL(NETDB, MEMP_NUM_NETDB, NETDB_ELEM_SIZE, "NETDB")
+#endif /* LWIP_DNS && LWIP_SOCKET */
+#if LWIP_DNS && DNS_LOCAL_HOSTLIST && DNS_LOCAL_HOSTLIST_IS_DYNAMIC
+LWIP_MEMPOOL(LOCALHOSTLIST, MEMP_NUM_LOCALHOSTLIST, LOCALHOSTLIST_ELEM_SIZE, "LOCALHOSTLIST")
+#endif /* LWIP_DNS && DNS_LOCAL_HOSTLIST && DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
+
+#if PPP_SUPPORT
+LWIP_MEMPOOL(PPP_PCB, MEMP_NUM_PPP_PCB, sizeof(ppp_pcb), "PPP_PCB")
+#if PPPOS_SUPPORT
+LWIP_MEMPOOL(PPPOS_PCB, MEMP_NUM_PPPOS_INTERFACES, sizeof(pppos_pcb), "PPPOS_PCB")
+#endif /* PPPOS_SUPPORT */
+#if PPPOE_SUPPORT
+LWIP_MEMPOOL(PPPOE_IF, MEMP_NUM_PPPOE_INTERFACES, sizeof(struct pppoe_softc), "PPPOE_IF")
+#endif /* PPPOE_SUPPORT */
+#if PPPOL2TP_SUPPORT
+LWIP_MEMPOOL(PPPOL2TP_PCB, MEMP_NUM_PPPOL2TP_INTERFACES, sizeof(pppol2tp_pcb), "PPPOL2TP_PCB")
+#endif /* PPPOL2TP_SUPPORT */
+#endif /* PPP_SUPPORT */
+
+#if LWIP_IPV6 && LWIP_ND6_QUEUEING
+LWIP_MEMPOOL(ND6_QUEUE, MEMP_NUM_ND6_QUEUE, sizeof(struct nd6_q_entry), "ND6_QUEUE")
+#endif /* LWIP_IPV6 && LWIP_ND6_QUEUEING */
+
+#if LWIP_IPV6 && LWIP_IPV6_REASS
+LWIP_MEMPOOL(IP6_REASSDATA, MEMP_NUM_REASSDATA, sizeof(struct ip6_reassdata), "IP6_REASSDATA")
+#endif /* LWIP_IPV6 && LWIP_IPV6_REASS */
+
+#if LWIP_IPV6 && LWIP_IPV6_MLD
+LWIP_MEMPOOL(MLD6_GROUP, MEMP_NUM_MLD6_GROUP, sizeof(struct mld_group), "MLD6_GROUP")
+#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */
+
+
+/*
+ * A list of pools of pbuf's used by LWIP.
+ *
+ * LWIP_PBUF_MEMPOOL(pool_name, number_elements, pbuf_payload_size, pool_description)
+ * creates a pool name MEMP_pool_name. description is used in stats.c
+ * This allocates enough space for the pbuf struct and a payload.
+ * (Example: pbuf_payload_size=0 allocates only size for the struct)
+ */
+LWIP_PBUF_MEMPOOL(PBUF, MEMP_NUM_PBUF, 0, "PBUF_REF/ROM")
+LWIP_PBUF_MEMPOOL(PBUF_POOL, PBUF_POOL_SIZE, PBUF_POOL_BUFSIZE, "PBUF_POOL")
+
+
+/*
+ * Allow for user-defined pools; this must be explicitly set in lwipopts.h
+ * since the default is to NOT look for lwippools.h
+ */
+#if MEMP_USE_CUSTOM_POOLS
+#include "lwippools.h"
+#endif /* MEMP_USE_CUSTOM_POOLS */
+
+/*
+ * REQUIRED CLEANUP: Clear up so we don't get "multiply defined" error later
+ * (#undef is ignored for something that is not defined)
+ */
+#undef LWIP_MEMPOOL
+#undef LWIP_MALLOC_MEMPOOL
+#undef LWIP_MALLOC_MEMPOOL_START
+#undef LWIP_MALLOC_MEMPOOL_END
+#undef LWIP_PBUF_MEMPOOL
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/mld6.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/mld6.h
new file mode 100644
index 0000000..7fa0797
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/mld6.h
@@ -0,0 +1,99 @@
+/**
+ * @file
+ *
+ * Multicast listener discovery for IPv6. Aims to be compliant with RFC 2710.
+ * No support for MLDv2.
+ */
+
+/*
+ * Copyright (c) 2010 Inico Technologies Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Ivan Delamer <delamer@inicotech.com>
+ *
+ *
+ * Please coordinate changes and requests with Ivan Delamer
+ * <delamer@inicotech.com>
+ */
+
+#ifndef LWIP_HDR_MLD6_H
+#define LWIP_HDR_MLD6_H
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV6_MLD && LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/pbuf.h"
+#include "lwip/netif.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** MLD group */
+struct mld_group {
+ /** next link */
+ struct mld_group *next;
+ /** multicast address */
+ ip6_addr_t group_address;
+ /** signifies we were the last person to report */
+ u8_t last_reporter_flag;
+ /** current state of the group */
+ u8_t group_state;
+ /** timer for reporting */
+ u16_t timer;
+ /** counter of simultaneous uses */
+ u8_t use;
+};
+
+#define MLD6_TMR_INTERVAL 100 /* Milliseconds */
+
+err_t mld6_stop(struct netif *netif);
+void mld6_report_groups(struct netif *netif);
+void mld6_tmr(void);
+struct mld_group *mld6_lookfor_group(struct netif *ifp, const ip6_addr_t *addr);
+void mld6_input(struct pbuf *p, struct netif *inp);
+err_t mld6_joingroup(const ip6_addr_t *srcaddr, const ip6_addr_t *groupaddr);
+err_t mld6_joingroup_netif(struct netif *netif, const ip6_addr_t *groupaddr);
+err_t mld6_leavegroup(const ip6_addr_t *srcaddr, const ip6_addr_t *groupaddr);
+err_t mld6_leavegroup_netif(struct netif *netif, const ip6_addr_t *groupaddr);
+
+/** @ingroup mld6
+ * Get list head of MLD6 groups for netif.
+ * Note: The allnodes group IP is NOT in the list, since it must always
+ * be received for correct IPv6 operation.
+ * @see @ref netif_set_mld_mac_filter()
+ */
+#define netif_mld6_data(netif) ((struct mld_group *)netif_get_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_MLD6))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_IPV6_MLD && LWIP_IPV6 */
+
+#endif /* LWIP_HDR_MLD6_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/nd6.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/nd6.h
new file mode 100644
index 0000000..8204fa4
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/nd6.h
@@ -0,0 +1,84 @@
+/**
+ * @file
+ *
+ * Neighbor discovery and stateless address autoconfiguration for IPv6.
+ * Aims to be compliant with RFC 4861 (Neighbor discovery) and RFC 4862
+ * (Address autoconfiguration).
+ */
+
+/*
+ * Copyright (c) 2010 Inico Technologies Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Ivan Delamer <delamer@inicotech.com>
+ *
+ *
+ * Please coordinate changes and requests with Ivan Delamer
+ * <delamer@inicotech.com>
+ */
+
+#ifndef LWIP_HDR_ND6_H
+#define LWIP_HDR_ND6_H
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/ip6_addr.h"
+#include "lwip/err.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** 1 second period */
+#define ND6_TMR_INTERVAL 1000
+
+struct pbuf;
+struct netif;
+
+void nd6_tmr(void);
+void nd6_input(struct pbuf *p, struct netif *inp);
+void nd6_clear_destination_cache(void);
+struct netif *nd6_find_route(const ip6_addr_t *ip6addr);
+err_t nd6_get_next_hop_addr_or_queue(struct netif *netif, struct pbuf *q, const ip6_addr_t *ip6addr, const u8_t **hwaddrp);
+u16_t nd6_get_destination_mtu(const ip6_addr_t *ip6addr, struct netif *netif);
+#if LWIP_ND6_TCP_REACHABILITY_HINTS
+void nd6_reachability_hint(const ip6_addr_t *ip6addr);
+#endif /* LWIP_ND6_TCP_REACHABILITY_HINTS */
+void nd6_cleanup_netif(struct netif *netif);
+#if LWIP_IPV6_MLD
+void nd6_adjust_mld_membership(struct netif *netif, s8_t addr_idx, u8_t new_state);
+#endif /* LWIP_IPV6_MLD */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_IPV6 */
+
+#endif /* LWIP_HDR_ND6_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/netbuf.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/netbuf.h
new file mode 100644
index 0000000..e6865f8
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/netbuf.h
@@ -0,0 +1,118 @@
+/**
+ * @file
+ * netbuf API (for netconn API)
+ */
+
+/*
+ * 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_HDR_NETBUF_H
+#define LWIP_HDR_NETBUF_H
+
+#include "lwip/opt.h"
+
+#if LWIP_NETCONN || LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */
+/* Note: Netconn API is always available when sockets are enabled -
+ * sockets are implemented on top of them */
+
+#include "lwip/pbuf.h"
+#include "lwip/ip_addr.h"
+#include "lwip/ip6_addr.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** This netbuf has dest-addr/port set */
+#define NETBUF_FLAG_DESTADDR 0x01
+/** This netbuf includes a checksum */
+#define NETBUF_FLAG_CHKSUM 0x02
+
+/** "Network buffer" - contains data and addressing info */
+struct netbuf {
+ struct pbuf *p, *ptr;
+ ip_addr_t addr;
+ u16_t port;
+#if LWIP_NETBUF_RECVINFO || LWIP_CHECKSUM_ON_COPY
+#if LWIP_CHECKSUM_ON_COPY
+ u8_t flags;
+#endif /* LWIP_CHECKSUM_ON_COPY */
+ u16_t toport_chksum;
+#if LWIP_NETBUF_RECVINFO
+ ip_addr_t toaddr;
+#endif /* LWIP_NETBUF_RECVINFO */
+#endif /* LWIP_NETBUF_RECVINFO || LWIP_CHECKSUM_ON_COPY */
+};
+
+/* Network buffer functions: */
+struct netbuf * netbuf_new (void);
+void netbuf_delete (struct netbuf *buf);
+void * netbuf_alloc (struct netbuf *buf, u16_t size);
+void netbuf_free (struct netbuf *buf);
+err_t netbuf_ref (struct netbuf *buf,
+ const void *dataptr, u16_t size);
+void netbuf_chain (struct netbuf *head, struct netbuf *tail);
+
+err_t netbuf_data (struct netbuf *buf,
+ void **dataptr, u16_t *len);
+s8_t netbuf_next (struct netbuf *buf);
+void netbuf_first (struct netbuf *buf);
+
+
+#define netbuf_copy_partial(buf, dataptr, len, offset) \
+ pbuf_copy_partial((buf)->p, (dataptr), (len), (offset))
+#define netbuf_copy(buf,dataptr,len) netbuf_copy_partial(buf, dataptr, len, 0)
+#define netbuf_take(buf, dataptr, len) pbuf_take((buf)->p, dataptr, len)
+#define netbuf_len(buf) ((buf)->p->tot_len)
+#define netbuf_fromaddr(buf) (&((buf)->addr))
+#define netbuf_set_fromaddr(buf, fromaddr) ip_addr_set(&((buf)->addr), fromaddr)
+#define netbuf_fromport(buf) ((buf)->port)
+#if LWIP_NETBUF_RECVINFO
+#define netbuf_destaddr(buf) (&((buf)->toaddr))
+#define netbuf_set_destaddr(buf, destaddr) ip_addr_set(&((buf)->toaddr), destaddr)
+#if LWIP_CHECKSUM_ON_COPY
+#define netbuf_destport(buf) (((buf)->flags & NETBUF_FLAG_DESTADDR) ? (buf)->toport_chksum : 0)
+#else /* LWIP_CHECKSUM_ON_COPY */
+#define netbuf_destport(buf) ((buf)->toport_chksum)
+#endif /* LWIP_CHECKSUM_ON_COPY */
+#endif /* LWIP_NETBUF_RECVINFO */
+#if LWIP_CHECKSUM_ON_COPY
+#define netbuf_set_chksum(buf, chksum) do { (buf)->flags = NETBUF_FLAG_CHKSUM; \
+ (buf)->toport_chksum = chksum; } while(0)
+#endif /* LWIP_CHECKSUM_ON_COPY */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_NETCONN || LWIP_SOCKET */
+
+#endif /* LWIP_HDR_NETBUF_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/netdb.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/netdb.h
new file mode 100644
index 0000000..d3d15df
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/netdb.h
@@ -0,0 +1,150 @@
+/**
+ * @file
+ * NETDB API (sockets)
+ */
+
+/*
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Simon Goldschmidt
+ *
+ */
+#ifndef LWIP_HDR_NETDB_H
+#define LWIP_HDR_NETDB_H
+
+#include "lwip/opt.h"
+
+#if LWIP_DNS && LWIP_SOCKET
+
+#include "lwip/arch.h"
+#include "lwip/inet.h"
+#include "lwip/sockets.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* some rarely used options */
+#ifndef LWIP_DNS_API_DECLARE_H_ERRNO
+#define LWIP_DNS_API_DECLARE_H_ERRNO 1
+#endif
+
+#ifndef LWIP_DNS_API_DEFINE_ERRORS
+#define LWIP_DNS_API_DEFINE_ERRORS 1
+#endif
+
+#ifndef LWIP_DNS_API_DEFINE_FLAGS
+#define LWIP_DNS_API_DEFINE_FLAGS 1
+#endif
+
+#ifndef LWIP_DNS_API_DECLARE_STRUCTS
+#define LWIP_DNS_API_DECLARE_STRUCTS 1
+#endif
+
+#if LWIP_DNS_API_DEFINE_ERRORS
+/** Errors used by the DNS API functions, h_errno can be one of them */
+#define EAI_NONAME 200
+#define EAI_SERVICE 201
+#define EAI_FAIL 202
+#define EAI_MEMORY 203
+#define EAI_FAMILY 204
+
+#define HOST_NOT_FOUND 210
+#define NO_DATA 211
+#define NO_RECOVERY 212
+#define TRY_AGAIN 213
+#endif /* LWIP_DNS_API_DEFINE_ERRORS */
+
+#if LWIP_DNS_API_DEFINE_FLAGS
+/* input flags for struct addrinfo */
+#define AI_PASSIVE 0x01
+#define AI_CANONNAME 0x02
+#define AI_NUMERICHOST 0x04
+#define AI_NUMERICSERV 0x08
+#define AI_V4MAPPED 0x10
+#define AI_ALL 0x20
+#define AI_ADDRCONFIG 0x40
+#endif /* LWIP_DNS_API_DEFINE_FLAGS */
+
+#if LWIP_DNS_API_DECLARE_STRUCTS
+struct hostent {
+ char *h_name; /* Official name of the host. */
+ char **h_aliases; /* A pointer to an array of pointers to alternative host names,
+ terminated by a null pointer. */
+ int h_addrtype; /* Address type. */
+ int h_length; /* The length, in bytes, of the address. */
+ char **h_addr_list; /* A pointer to an array of pointers to network addresses (in
+ network byte order) for the host, terminated by a null pointer. */
+#define h_addr h_addr_list[0] /* for backward compatibility */
+};
+
+struct addrinfo {
+ int ai_flags; /* Input flags. */
+ int ai_family; /* Address family of socket. */
+ int ai_socktype; /* Socket type. */
+ int ai_protocol; /* Protocol of socket. */
+ socklen_t ai_addrlen; /* Length of socket address. */
+ struct sockaddr *ai_addr; /* Socket address of socket. */
+ char *ai_canonname; /* Canonical name of service location. */
+ struct addrinfo *ai_next; /* Pointer to next in list. */
+};
+#endif /* LWIP_DNS_API_DECLARE_STRUCTS */
+
+#define NETDB_ELEM_SIZE (sizeof(struct addrinfo) + sizeof(struct sockaddr_storage) + DNS_MAX_NAME_LENGTH + 1)
+
+#if LWIP_DNS_API_DECLARE_H_ERRNO
+/* application accessible error code set by the DNS API functions */
+extern int h_errno;
+#endif /* LWIP_DNS_API_DECLARE_H_ERRNO*/
+
+struct hostent *lwip_gethostbyname(const char *name);
+int lwip_gethostbyname_r(const char *name, struct hostent *ret, char *buf,
+ size_t buflen, struct hostent **result, int *h_errnop);
+void lwip_freeaddrinfo(struct addrinfo *ai);
+int lwip_getaddrinfo(const char *nodename,
+ const char *servname,
+ const struct addrinfo *hints,
+ struct addrinfo **res);
+
+#if LWIP_COMPAT_SOCKETS
+/** @ingroup netdbapi */
+#define gethostbyname(name) lwip_gethostbyname(name)
+/** @ingroup netdbapi */
+#define gethostbyname_r(name, ret, buf, buflen, result, h_errnop) \
+ lwip_gethostbyname_r(name, ret, buf, buflen, result, h_errnop)
+/** @ingroup netdbapi */
+#define freeaddrinfo(addrinfo) lwip_freeaddrinfo(addrinfo)
+/** @ingroup netdbapi */
+#define getaddrinfo(nodname, servname, hints, res) \
+ lwip_getaddrinfo(nodname, servname, hints, res)
+#endif /* LWIP_COMPAT_SOCKETS */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_DNS && LWIP_SOCKET */
+
+#endif /* LWIP_HDR_NETDB_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/netif.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/netif.h
new file mode 100644
index 0000000..67a2d24
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/netif.h
@@ -0,0 +1,474 @@
+/**
+ * @file
+ * netif API (to be used from TCPIP thread)
+ */
+
+/*
+ * 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_HDR_NETIF_H
+#define LWIP_HDR_NETIF_H
+
+#include "lwip/opt.h"
+
+#define ENABLE_LOOPBACK (LWIP_NETIF_LOOPBACK || LWIP_HAVE_LOOPIF)
+
+#include "lwip/err.h"
+
+#include "lwip/ip_addr.h"
+
+#include "lwip/def.h"
+#include "lwip/pbuf.h"
+#include "lwip/stats.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Throughout this file, IP addresses are expected to be in
+ * the same byte order as in IP_PCB. */
+
+/** Must be the maximum of all used hardware address lengths
+ across all types of interfaces in use.
+ This does not have to be changed, normally. */
+#ifndef NETIF_MAX_HWADDR_LEN
+#define NETIF_MAX_HWADDR_LEN 6U
+#endif
+
+/**
+ * @defgroup netif_flags Flags
+ * @ingroup netif
+ * @{
+ */
+
+/** Whether the network interface is 'up'. This is
+ * a software flag used to control whether this network
+ * interface is enabled and processes traffic.
+ * It must be set by the startup code before this netif can be used
+ * (also for dhcp/autoip).
+ */
+#define NETIF_FLAG_UP 0x01U
+/** If set, the netif has broadcast capability.
+ * Set by the netif driver in its init function. */
+#define NETIF_FLAG_BROADCAST 0x02U
+/** If set, the interface has an active link
+ * (set by the network interface driver).
+ * Either set by the netif driver in its init function (if the link
+ * is up at that time) or at a later point once the link comes up
+ * (if link detection is supported by the hardware). */
+#define NETIF_FLAG_LINK_UP 0x04U
+/** If set, the netif is an ethernet device using ARP.
+ * Set by the netif driver in its init function.
+ * Used to check input packet types and use of DHCP. */
+#define NETIF_FLAG_ETHARP 0x08U
+/** If set, the netif is an ethernet device. It might not use
+ * ARP or TCP/IP if it is used for PPPoE only.
+ */
+#define NETIF_FLAG_ETHERNET 0x10U
+/** If set, the netif has IGMP capability.
+ * Set by the netif driver in its init function. */
+#define NETIF_FLAG_IGMP 0x20U
+/** If set, the netif has MLD6 capability.
+ * Set by the netif driver in its init function. */
+#define NETIF_FLAG_MLD6 0x40U
+
+/**
+ * @}
+ */
+
+enum lwip_internal_netif_client_data_index
+{
+#if LWIP_DHCP
+ LWIP_NETIF_CLIENT_DATA_INDEX_DHCP,
+#endif
+#if LWIP_AUTOIP
+ LWIP_NETIF_CLIENT_DATA_INDEX_AUTOIP,
+#endif
+#if LWIP_IGMP
+ LWIP_NETIF_CLIENT_DATA_INDEX_IGMP,
+#endif
+#if LWIP_IPV6_MLD
+ LWIP_NETIF_CLIENT_DATA_INDEX_MLD6,
+#endif
+ LWIP_NETIF_CLIENT_DATA_INDEX_MAX
+};
+
+#if LWIP_CHECKSUM_CTRL_PER_NETIF
+#define NETIF_CHECKSUM_GEN_IP 0x0001
+#define NETIF_CHECKSUM_GEN_UDP 0x0002
+#define NETIF_CHECKSUM_GEN_TCP 0x0004
+#define NETIF_CHECKSUM_GEN_ICMP 0x0008
+#define NETIF_CHECKSUM_GEN_ICMP6 0x0010
+#define NETIF_CHECKSUM_CHECK_IP 0x0100
+#define NETIF_CHECKSUM_CHECK_UDP 0x0200
+#define NETIF_CHECKSUM_CHECK_TCP 0x0400
+#define NETIF_CHECKSUM_CHECK_ICMP 0x0800
+#define NETIF_CHECKSUM_CHECK_ICMP6 0x1000
+#define NETIF_CHECKSUM_ENABLE_ALL 0xFFFF
+#define NETIF_CHECKSUM_DISABLE_ALL 0x0000
+#endif /* LWIP_CHECKSUM_CTRL_PER_NETIF */
+
+struct netif;
+
+/** MAC Filter Actions, these are passed to a netif's igmp_mac_filter or
+ * mld_mac_filter callback function. */
+enum netif_mac_filter_action {
+ /** Delete a filter entry */
+ NETIF_DEL_MAC_FILTER = 0,
+ /** Add a filter entry */
+ NETIF_ADD_MAC_FILTER = 1
+};
+
+/** Function prototype for netif init functions. Set up flags and output/linkoutput
+ * callback functions in this function.
+ *
+ * @param netif The netif to initialize
+ */
+typedef err_t (*netif_init_fn)(struct netif *netif);
+/** Function prototype for netif->input functions. This function is saved as 'input'
+ * callback function in the netif struct. Call it when a packet has been received.
+ *
+ * @param p The received packet, copied into a pbuf
+ * @param inp The netif which received the packet
+ */
+typedef err_t (*netif_input_fn)(struct pbuf *p, struct netif *inp);
+
+#if LWIP_IPV4
+/** Function prototype for netif->output functions. Called by lwIP when a packet
+ * shall be sent. For ethernet netif, set this to 'etharp_output' and set
+ * 'linkoutput'.
+ *
+ * @param netif The netif which shall send a packet
+ * @param p The packet to send (p->payload points to IP header)
+ * @param ipaddr The IP address to which the packet shall be sent
+ */
+typedef err_t (*netif_output_fn)(struct netif *netif, struct pbuf *p,
+ const ip4_addr_t *ipaddr);
+#endif /* LWIP_IPV4*/
+
+#if LWIP_IPV6
+/** Function prototype for netif->output_ip6 functions. Called by lwIP when a packet
+ * shall be sent. For ethernet netif, set this to 'ethip6_output' and set
+ * 'linkoutput'.
+ *
+ * @param netif The netif which shall send a packet
+ * @param p The packet to send (p->payload points to IP header)
+ * @param ipaddr The IPv6 address to which the packet shall be sent
+ */
+typedef err_t (*netif_output_ip6_fn)(struct netif *netif, struct pbuf *p,
+ const ip6_addr_t *ipaddr);
+#endif /* LWIP_IPV6 */
+
+/** Function prototype for netif->linkoutput functions. Only used for ethernet
+ * netifs. This function is called by ARP when a packet shall be sent.
+ *
+ * @param netif The netif which shall send a packet
+ * @param p The packet to send (raw ethernet packet)
+ */
+typedef err_t (*netif_linkoutput_fn)(struct netif *netif, struct pbuf *p);
+/** Function prototype for netif status- or link-callback functions. */
+typedef void (*netif_status_callback_fn)(struct netif *netif);
+#if LWIP_IPV4 && LWIP_IGMP
+/** Function prototype for netif igmp_mac_filter functions */
+typedef err_t (*netif_igmp_mac_filter_fn)(struct netif *netif,
+ const ip4_addr_t *group, enum netif_mac_filter_action action);
+#endif /* LWIP_IPV4 && LWIP_IGMP */
+#if LWIP_IPV6 && LWIP_IPV6_MLD
+/** Function prototype for netif mld_mac_filter functions */
+typedef err_t (*netif_mld_mac_filter_fn)(struct netif *netif,
+ const ip6_addr_t *group, enum netif_mac_filter_action action);
+#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */
+
+#if LWIP_DHCP || LWIP_AUTOIP || LWIP_IGMP || LWIP_IPV6_MLD || (LWIP_NUM_NETIF_CLIENT_DATA > 0)
+u8_t netif_alloc_client_data_id(void);
+/** @ingroup netif_cd
+ * Set client data. Obtain ID from netif_alloc_client_data_id().
+ */
+#define netif_set_client_data(netif, id, data) netif_get_client_data(netif, id) = (data)
+/** @ingroup netif_cd
+ * Get client data. Obtain ID from netif_alloc_client_data_id().
+ */
+#define netif_get_client_data(netif, id) (netif)->client_data[(id)]
+#endif /* LWIP_DHCP || LWIP_AUTOIP || (LWIP_NUM_NETIF_CLIENT_DATA > 0) */
+
+/** Generic data structure used for all lwIP network interfaces.
+ * The following fields should be filled in by the initialization
+ * function for the device driver: hwaddr_len, hwaddr[], mtu, flags */
+struct netif {
+ /** pointer to next in linked list */
+ struct netif *next;
+
+#if LWIP_IPV4
+ /** IP address configuration in network byte order */
+ ip_addr_t ip_addr;
+ ip_addr_t netmask;
+ ip_addr_t gw;
+#endif /* LWIP_IPV4 */
+#if LWIP_IPV6
+ /** Array of IPv6 addresses for this netif. */
+ ip_addr_t ip6_addr[LWIP_IPV6_NUM_ADDRESSES];
+ /** The state of each IPv6 address (Tentative, Preferred, etc).
+ * @see ip6_addr.h */
+ u8_t ip6_addr_state[LWIP_IPV6_NUM_ADDRESSES];
+#endif /* LWIP_IPV6 */
+ /** This function is called by the network device driver
+ * to pass a packet up the TCP/IP stack. */
+ netif_input_fn input;
+#if LWIP_IPV4
+ /** This function is called by the IP module when it wants
+ * to send a packet on the interface. This function typically
+ * first resolves the hardware address, then sends the packet.
+ * For ethernet physical layer, this is usually etharp_output() */
+ netif_output_fn output;
+#endif /* LWIP_IPV4 */
+ /** This function is called by ethernet_output() when it wants
+ * to send a packet on the interface. This function outputs
+ * the pbuf as-is on the link medium. */
+ netif_linkoutput_fn linkoutput;
+#if LWIP_IPV6
+ /** This function is called by the IPv6 module when it wants
+ * to send a packet on the interface. This function typically
+ * first resolves the hardware address, then sends the packet.
+ * For ethernet physical layer, this is usually ethip6_output() */
+ netif_output_ip6_fn output_ip6;
+#endif /* LWIP_IPV6 */
+#if LWIP_NETIF_STATUS_CALLBACK
+ /** This function is called when the netif state is set to up or down
+ */
+ netif_status_callback_fn status_callback;
+#endif /* LWIP_NETIF_STATUS_CALLBACK */
+#if LWIP_NETIF_LINK_CALLBACK
+ /** This function is called when the netif link is set to up or down
+ */
+ netif_status_callback_fn link_callback;
+#endif /* LWIP_NETIF_LINK_CALLBACK */
+#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;
+#ifdef netif_get_client_data
+ void* client_data[LWIP_NETIF_CLIENT_DATA_INDEX_MAX + LWIP_NUM_NETIF_CLIENT_DATA];
+#endif
+#if LWIP_IPV6_AUTOCONFIG
+ /** is this netif enabled for IPv6 autoconfiguration */
+ u8_t ip6_autoconfig_enabled;
+#endif /* LWIP_IPV6_AUTOCONFIG */
+#if LWIP_IPV6_SEND_ROUTER_SOLICIT
+ /** Number of Router Solicitation messages that remain to be sent. */
+ u8_t rs_count;
+#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */
+#if LWIP_NETIF_HOSTNAME
+ /* the hostname for this netif, NULL is a valid value */
+ const char* hostname;
+#endif /* LWIP_NETIF_HOSTNAME */
+#if LWIP_CHECKSUM_CTRL_PER_NETIF
+ u16_t chksum_flags;
+#endif /* LWIP_CHECKSUM_CTRL_PER_NETIF*/
+ /** maximum transfer unit (in bytes) */
+ u16_t mtu;
+ /** number of bytes used in hwaddr */
+ u8_t hwaddr_len;
+ /** link level hardware address of this interface */
+ u8_t hwaddr[NETIF_MAX_HWADDR_LEN];
+ /** flags (@see @ref netif_flags) */
+ u8_t flags;
+ /** descriptive abbreviation */
+ char name[2];
+ /** number of this interface */
+ u8_t num;
+#if MIB2_STATS
+ /** link type (from "snmp_ifType" enum from snmp_mib2.h) */
+ u8_t link_type;
+ /** (estimate) link speed */
+ u32_t link_speed;
+ /** timestamp at last change made (up/down) */
+ u32_t ts;
+ /** counters */
+ struct stats_mib2_netif_ctrs mib2_counters;
+#endif /* MIB2_STATS */
+#if LWIP_IPV4 && LWIP_IGMP
+ /** This function could be called to add or delete an entry in the multicast
+ filter table of the ethernet MAC.*/
+ netif_igmp_mac_filter_fn igmp_mac_filter;
+#endif /* LWIP_IPV4 && LWIP_IGMP */
+#if LWIP_IPV6 && LWIP_IPV6_MLD
+ /** This function could be called to add or delete an entry in the IPv6 multicast
+ filter table of the ethernet MAC. */
+ netif_mld_mac_filter_fn mld_mac_filter;
+#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */
+#if LWIP_NETIF_HWADDRHINT
+ u8_t *addr_hint;
+#endif /* LWIP_NETIF_HWADDRHINT */
+#if ENABLE_LOOPBACK
+ /* List of packets to be queued for ourselves. */
+ struct pbuf *loop_first;
+ struct pbuf *loop_last;
+#if LWIP_LOOPBACK_MAX_PBUFS
+ u16_t loop_cnt_current;
+#endif /* LWIP_LOOPBACK_MAX_PBUFS */
+#endif /* ENABLE_LOOPBACK */
+};
+
+#if LWIP_CHECKSUM_CTRL_PER_NETIF
+#define NETIF_SET_CHECKSUM_CTRL(netif, chksumflags) do { \
+ (netif)->chksum_flags = chksumflags; } while(0)
+#define IF__NETIF_CHECKSUM_ENABLED(netif, chksumflag) if (((netif) == NULL) || (((netif)->chksum_flags & (chksumflag)) != 0))
+#else /* LWIP_CHECKSUM_CTRL_PER_NETIF */
+#define NETIF_SET_CHECKSUM_CTRL(netif, chksumflags)
+#define IF__NETIF_CHECKSUM_ENABLED(netif, chksumflag)
+#endif /* LWIP_CHECKSUM_CTRL_PER_NETIF */
+
+/** The list of network interfaces. */
+extern struct netif *netif_list;
+/** The default network interface. */
+extern struct netif *netif_default;
+
+void netif_init(void);
+
+struct netif *netif_add(struct netif *netif,
+#if LWIP_IPV4
+ const ip4_addr_t *ipaddr, const ip4_addr_t *netmask, const ip4_addr_t *gw,
+#endif /* LWIP_IPV4 */
+ void *state, netif_init_fn init, netif_input_fn input);
+#if LWIP_IPV4
+void netif_set_addr(struct netif *netif, const ip4_addr_t *ipaddr, const ip4_addr_t *netmask,
+ const ip4_addr_t *gw);
+#endif /* LWIP_IPV4 */
+void netif_remove(struct netif * netif);
+
+/* Returns a network interface given its name. The name is of the form
+ "et0", where the first two letters are the "name" field in the
+ netif structure, and the digit is in the num field in the same
+ structure. */
+struct netif *netif_find(const char *name);
+
+void netif_set_default(struct netif *netif);
+
+#if LWIP_IPV4
+void netif_set_ipaddr(struct netif *netif, const ip4_addr_t *ipaddr);
+void netif_set_netmask(struct netif *netif, const ip4_addr_t *netmask);
+void netif_set_gw(struct netif *netif, const ip4_addr_t *gw);
+/** @ingroup netif_ip4 */
+#define netif_ip4_addr(netif) ((const ip4_addr_t*)ip_2_ip4(&((netif)->ip_addr)))
+/** @ingroup netif_ip4 */
+#define netif_ip4_netmask(netif) ((const ip4_addr_t*)ip_2_ip4(&((netif)->netmask)))
+/** @ingroup netif_ip4 */
+#define netif_ip4_gw(netif) ((const ip4_addr_t*)ip_2_ip4(&((netif)->gw)))
+/** @ingroup netif_ip4 */
+#define netif_ip_addr4(netif) ((const ip_addr_t*)&((netif)->ip_addr))
+/** @ingroup netif_ip4 */
+#define netif_ip_netmask4(netif) ((const ip_addr_t*)&((netif)->netmask))
+/** @ingroup netif_ip4 */
+#define netif_ip_gw4(netif) ((const ip_addr_t*)&((netif)->gw))
+#endif /* LWIP_IPV4 */
+
+void netif_set_up(struct netif *netif);
+void netif_set_down(struct netif *netif);
+/** @ingroup netif
+ * Ask if an interface is up
+ */
+#define netif_is_up(netif) (((netif)->flags & NETIF_FLAG_UP) ? (u8_t)1 : (u8_t)0)
+
+#if LWIP_NETIF_STATUS_CALLBACK
+void netif_set_status_callback(struct netif *netif, netif_status_callback_fn status_callback);
+#endif /* LWIP_NETIF_STATUS_CALLBACK */
+#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);
+/** Ask if a link is up */
+#define netif_is_link_up(netif) (((netif)->flags & NETIF_FLAG_LINK_UP) ? (u8_t)1 : (u8_t)0)
+
+#if LWIP_NETIF_LINK_CALLBACK
+void netif_set_link_callback(struct netif *netif, netif_status_callback_fn link_callback);
+#endif /* LWIP_NETIF_LINK_CALLBACK */
+
+#if LWIP_NETIF_HOSTNAME
+/** @ingroup netif */
+#define netif_set_hostname(netif, name) do { if((netif) != NULL) { (netif)->hostname = name; }}while(0)
+/** @ingroup netif */
+#define netif_get_hostname(netif) (((netif) != NULL) ? ((netif)->hostname) : NULL)
+#endif /* LWIP_NETIF_HOSTNAME */
+
+#if LWIP_IGMP
+/** @ingroup netif */
+#define netif_set_igmp_mac_filter(netif, function) do { if((netif) != NULL) { (netif)->igmp_mac_filter = function; }}while(0)
+#define netif_get_igmp_mac_filter(netif) (((netif) != NULL) ? ((netif)->igmp_mac_filter) : NULL)
+#endif /* LWIP_IGMP */
+
+#if LWIP_IPV6 && LWIP_IPV6_MLD
+/** @ingroup netif */
+#define netif_set_mld_mac_filter(netif, function) do { if((netif) != NULL) { (netif)->mld_mac_filter = function; }}while(0)
+#define netif_get_mld_mac_filter(netif) (((netif) != NULL) ? ((netif)->mld_mac_filter) : NULL)
+#define netif_mld_mac_filter(netif, addr, action) do { if((netif) && (netif)->mld_mac_filter) { (netif)->mld_mac_filter((netif), (addr), (action)); }}while(0)
+#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */
+
+#if ENABLE_LOOPBACK
+err_t netif_loop_output(struct netif *netif, struct pbuf *p);
+void netif_poll(struct netif *netif);
+#if !LWIP_NETIF_LOOPBACK_MULTITHREADING
+void netif_poll_all(void);
+#endif /* !LWIP_NETIF_LOOPBACK_MULTITHREADING */
+#endif /* ENABLE_LOOPBACK */
+
+err_t netif_input(struct pbuf *p, struct netif *inp);
+
+#if LWIP_IPV6
+/** @ingroup netif_ip6 */
+#define netif_ip_addr6(netif, i) ((const ip_addr_t*)(&((netif)->ip6_addr[i])))
+/** @ingroup netif_ip6 */
+#define netif_ip6_addr(netif, i) ((const ip6_addr_t*)ip_2_ip6(&((netif)->ip6_addr[i])))
+void netif_ip6_addr_set(struct netif *netif, s8_t addr_idx, const ip6_addr_t *addr6);
+void netif_ip6_addr_set_parts(struct netif *netif, s8_t addr_idx, u32_t i0, u32_t i1, u32_t i2, u32_t i3);
+#define netif_ip6_addr_state(netif, i) ((netif)->ip6_addr_state[i])
+void netif_ip6_addr_set_state(struct netif* netif, s8_t addr_idx, u8_t state);
+s8_t netif_get_ip6_addr_match(struct netif *netif, const ip6_addr_t *ip6addr);
+void netif_create_ip6_linklocal_address(struct netif *netif, u8_t from_mac_48bit);
+err_t netif_add_ip6_address(struct netif *netif, const ip6_addr_t *ip6addr, s8_t *chosen_idx);
+#define netif_set_ip6_autoconfig_enabled(netif, action) do { if(netif) { (netif)->ip6_autoconfig_enabled = (action); }}while(0)
+#endif /* LWIP_IPV6 */
+
+#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
+
+#endif /* LWIP_HDR_NETIF_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/netifapi.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/netifapi.h
new file mode 100644
index 0000000..8bd2b4f
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/netifapi.h
@@ -0,0 +1,140 @@
+/**
+ * @file
+ * netif API (to be used from non-TCPIP threads)
+ */
+
+/*
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ */
+#ifndef LWIP_HDR_NETIFAPI_H
+#define LWIP_HDR_NETIFAPI_H
+
+#include "lwip/opt.h"
+
+#if LWIP_NETIF_API /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/sys.h"
+#include "lwip/netif.h"
+#include "lwip/dhcp.h"
+#include "lwip/autoip.h"
+#include "lwip/priv/tcpip_priv.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if LWIP_MPU_COMPATIBLE
+#define NETIFAPI_IPADDR_DEF(type, m) type m
+#else /* LWIP_MPU_COMPATIBLE */
+#define NETIFAPI_IPADDR_DEF(type, m) const type * m
+#endif /* LWIP_MPU_COMPATIBLE */
+
+typedef void (*netifapi_void_fn)(struct netif *netif);
+typedef err_t (*netifapi_errt_fn)(struct netif *netif);
+
+struct netifapi_msg {
+ struct tcpip_api_call_data call;
+ struct netif *netif;
+ union {
+ struct {
+#if LWIP_IPV4
+ NETIFAPI_IPADDR_DEF(ip4_addr_t, ipaddr);
+ NETIFAPI_IPADDR_DEF(ip4_addr_t, netmask);
+ NETIFAPI_IPADDR_DEF(ip4_addr_t, gw);
+#endif /* LWIP_IPV4 */
+ void *state;
+ netif_init_fn init;
+ netif_input_fn input;
+ } add;
+ struct {
+ netifapi_void_fn voidfunc;
+ netifapi_errt_fn errtfunc;
+ } common;
+ } msg;
+};
+
+
+/* API for application */
+err_t netifapi_netif_add(struct netif *netif,
+#if LWIP_IPV4
+ const ip4_addr_t *ipaddr, const ip4_addr_t *netmask, const ip4_addr_t *gw,
+#endif /* LWIP_IPV4 */
+ void *state, netif_init_fn init, netif_input_fn input);
+
+#if LWIP_IPV4
+err_t netifapi_netif_set_addr(struct netif *netif, const ip4_addr_t *ipaddr,
+ const ip4_addr_t *netmask, const ip4_addr_t *gw);
+#endif /* LWIP_IPV4*/
+
+err_t netifapi_netif_common(struct netif *netif, netifapi_void_fn voidfunc,
+ netifapi_errt_fn errtfunc);
+
+/** @ingroup netifapi_netif */
+#define netifapi_netif_remove(n) netifapi_netif_common(n, netif_remove, NULL)
+/** @ingroup netifapi_netif */
+#define netifapi_netif_set_up(n) netifapi_netif_common(n, netif_set_up, NULL)
+/** @ingroup netifapi_netif */
+#define netifapi_netif_set_down(n) netifapi_netif_common(n, netif_set_down, NULL)
+/** @ingroup netifapi_netif */
+#define netifapi_netif_set_default(n) netifapi_netif_common(n, netif_set_default, NULL)
+/** @ingroup netifapi_netif */
+#define netifapi_netif_set_link_up(n) netifapi_netif_common(n, netif_set_link_up, NULL)
+/** @ingroup netifapi_netif */
+#define netifapi_netif_set_link_down(n) netifapi_netif_common(n, netif_set_link_down, NULL)
+
+/**
+ * @defgroup netifapi_dhcp4 DHCPv4
+ * @ingroup netifapi
+ * To be called from non-TCPIP threads
+ */
+/** @ingroup netifapi_dhcp4 */
+#define netifapi_dhcp_start(n) netifapi_netif_common(n, NULL, dhcp_start)
+/** @ingroup netifapi_dhcp4 */
+#define netifapi_dhcp_stop(n) netifapi_netif_common(n, dhcp_stop, NULL)
+/** @ingroup netifapi_dhcp4 */
+#define netifapi_dhcp_inform(n) netifapi_netif_common(n, dhcp_inform, NULL)
+/** @ingroup netifapi_dhcp4 */
+#define netifapi_dhcp_renew(n) netifapi_netif_common(n, NULL, dhcp_renew)
+/** @ingroup netifapi_dhcp4 */
+#define netifapi_dhcp_release(n) netifapi_netif_common(n, NULL, dhcp_release)
+
+/**
+ * @defgroup netifapi_autoip AUTOIP
+ * @ingroup netifapi
+ * To be called from non-TCPIP threads
+ */
+/** @ingroup netifapi_autoip */
+#define netifapi_autoip_start(n) netifapi_netif_common(n, NULL, autoip_start)
+/** @ingroup netifapi_autoip */
+#define netifapi_autoip_stop(n) netifapi_netif_common(n, NULL, autoip_stop)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_NETIF_API */
+
+#endif /* LWIP_HDR_NETIFAPI_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/opt.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/opt.h
new file mode 100644
index 0000000..fd459af
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/opt.h
@@ -0,0 +1,2876 @@
+/**
+ * @file
+ *
+ * lwIP Options Configuration
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+/*
+ * NOTE: || defined __DOXYGEN__ is a workaround for doxygen bug -
+ * without this, doxygen does not see the actual #define
+ */
+
+#if !defined LWIP_HDR_OPT_H
+#define LWIP_HDR_OPT_H
+
+/*
+ * Include user defined options first. Anything not defined in these files
+ * will be set to standard values. Override anything you don't like!
+ */
+#include "lwipopts.h"
+#include "lwip/debug.h"
+
+/**
+ * @defgroup lwip_opts Options (lwipopts.h)
+ * @ingroup lwip
+ *
+ * @defgroup lwip_opts_debug Debugging
+ * @ingroup lwip_opts
+ *
+ * @defgroup lwip_opts_infrastructure Infrastructure
+ * @ingroup lwip_opts
+ *
+ * @defgroup lwip_opts_callback Callback-style APIs
+ * @ingroup lwip_opts
+ *
+ * @defgroup lwip_opts_threadsafe_apis Thread-safe APIs
+ * @ingroup lwip_opts
+ */
+
+ /*
+ ------------------------------------
+ -------------- NO SYS --------------
+ ------------------------------------
+*/
+/**
+ * @defgroup lwip_opts_nosys NO_SYS
+ * @ingroup lwip_opts_infrastructure
+ * @{
+ */
+/**
+ * NO_SYS==1: Use lwIP without OS-awareness (no thread, semaphores, mutexes or
+ * mboxes). This means threaded APIs cannot be used (socket, netconn,
+ * i.e. everything in the 'api' folder), only the callback-style raw API is
+ * available (and you have to watch out for yourself that you don't access
+ * lwIP functions/structures from more than one context at a time!)
+ */
+#if !defined NO_SYS || defined __DOXYGEN__
+#define NO_SYS 0
+#endif
+/**
+ * @}
+ */
+
+/**
+ * @defgroup lwip_opts_timers Timers
+ * @ingroup lwip_opts_infrastructure
+ * @{
+ */
+/**
+ * LWIP_TIMERS==0: Drop support for sys_timeout and lwip-internal cyclic timers.
+ * (the array of lwip-internal cyclic timers is still provided)
+ * (check NO_SYS_NO_TIMERS for compatibility to old versions)
+ */
+#if !defined LWIP_TIMERS || defined __DOXYGEN__
+#ifdef NO_SYS_NO_TIMERS
+#define LWIP_TIMERS (!NO_SYS || (NO_SYS && !NO_SYS_NO_TIMERS))
+#else
+#define LWIP_TIMERS 1
+#endif
+#endif
+
+/**
+ * LWIP_TIMERS_CUSTOM==1: Provide your own timer implementation.
+ * Function prototypes in timeouts.h and the array of lwip-internal cyclic timers
+ * are still included, but the implementation is not. The following functions
+ * will be required: sys_timeouts_init(), sys_timeout(), sys_untimeout(),
+ * sys_timeouts_mbox_fetch()
+ */
+#if !defined LWIP_TIMERS_CUSTOM || defined __DOXYGEN__
+#define LWIP_TIMERS_CUSTOM 0
+#endif
+/**
+ * @}
+ */
+
+/**
+ * @defgroup lwip_opts_memcpy memcpy
+ * @ingroup lwip_opts_infrastructure
+ * @{
+ */
+/**
+ * MEMCPY: override this if you have a faster implementation at hand than the
+ * one included in your C library
+ */
+#if !defined MEMCPY || defined __DOXYGEN__
+#define MEMCPY(dst,src,len) memcpy(dst,src,len)
+#endif
+
+/**
+ * SMEMCPY: override this with care! Some compilers (e.g. gcc) can inline a
+ * call to memcpy() if the length is known at compile time and is small.
+ */
+#if !defined SMEMCPY || defined __DOXYGEN__
+#define SMEMCPY(dst,src,len) memcpy(dst,src,len)
+#endif
+/**
+ * @}
+ */
+
+/*
+ ------------------------------------
+ ----------- Core locking -----------
+ ------------------------------------
+*/
+/**
+ * @defgroup lwip_opts_lock Core locking and MPU
+ * @ingroup lwip_opts_infrastructure
+ * @{
+ */
+/**
+ * LWIP_MPU_COMPATIBLE: enables special memory management mechanism
+ * which makes lwip able to work on MPU (Memory Protection Unit) system
+ * by not passing stack-pointers to other threads
+ * (this decreases performance as memory is allocated from pools instead
+ * of keeping it on the stack)
+ */
+#if !defined LWIP_MPU_COMPATIBLE || defined __DOXYGEN__
+#define LWIP_MPU_COMPATIBLE 0
+#endif
+
+/**
+ * LWIP_TCPIP_CORE_LOCKING
+ * Creates a global mutex that is held during TCPIP thread operations.
+ * Can be locked by client code to perform lwIP operations without changing
+ * into TCPIP thread using callbacks. See LOCK_TCPIP_CORE() and
+ * UNLOCK_TCPIP_CORE().
+ * Your system should provide mutexes supporting priority inversion to use this.
+ */
+#if !defined LWIP_TCPIP_CORE_LOCKING || defined __DOXYGEN__
+#define LWIP_TCPIP_CORE_LOCKING 1
+#endif
+
+/**
+ * LWIP_TCPIP_CORE_LOCKING_INPUT: when LWIP_TCPIP_CORE_LOCKING is enabled,
+ * this lets tcpip_input() grab the mutex for input packets as well,
+ * instead of allocating a message and passing it to tcpip_thread.
+ *
+ * ATTENTION: this does not work when tcpip_input() is called from
+ * interrupt context!
+ */
+#if !defined LWIP_TCPIP_CORE_LOCKING_INPUT || defined __DOXYGEN__
+#define LWIP_TCPIP_CORE_LOCKING_INPUT 0
+#endif
+
+/**
+ * SYS_LIGHTWEIGHT_PROT==1: enable inter-task protection (and task-vs-interrupt
+ * protection) for certain critical regions during buffer allocation, deallocation
+ * and memory allocation and deallocation.
+ * ATTENTION: This is required when using lwIP from more than one context! If
+ * you disable this, you must be sure what you are doing!
+ */
+#if !defined SYS_LIGHTWEIGHT_PROT || defined __DOXYGEN__
+#define SYS_LIGHTWEIGHT_PROT 1
+#endif
+/**
+ * @}
+ */
+
+/*
+ ------------------------------------
+ ---------- Memory options ----------
+ ------------------------------------
+*/
+/**
+ * @defgroup lwip_opts_mem Heap and memory pools
+ * @ingroup lwip_opts_infrastructure
+ * @{
+ */
+/**
+ * MEM_LIBC_MALLOC==1: Use malloc/free/realloc provided by your C-library
+ * instead of the lwip internal allocator. Can save code size if you
+ * already use it.
+ */
+#if !defined MEM_LIBC_MALLOC || defined __DOXYGEN__
+#define MEM_LIBC_MALLOC 0
+#endif
+
+/**
+ * MEMP_MEM_MALLOC==1: Use mem_malloc/mem_free instead of the lwip pool allocator.
+ * Especially useful with MEM_LIBC_MALLOC but handle with care regarding execution
+ * speed (heap alloc can be much slower than pool alloc) and usage from interrupts
+ * (especially if your netif driver allocates PBUF_POOL pbufs for received frames
+ * from interrupt)!
+ * ATTENTION: Currently, this uses the heap for ALL pools (also for private pools,
+ * not only for internal pools defined in memp_std.h)!
+ */
+#if !defined MEMP_MEM_MALLOC || defined __DOXYGEN__
+#define MEMP_MEM_MALLOC 0
+#endif
+
+/**
+ * MEM_ALIGNMENT: should be set to the alignment of the CPU
+ * 4 byte alignment -> \#define MEM_ALIGNMENT 4
+ * 2 byte alignment -> \#define MEM_ALIGNMENT 2
+ */
+#if !defined MEM_ALIGNMENT || defined __DOXYGEN__
+#define MEM_ALIGNMENT 1
+#endif
+
+/**
+ * MEM_SIZE: the size of the heap memory. If the application will send
+ * a lot of data that needs to be copied, this should be set high.
+ */
+#if !defined MEM_SIZE || defined __DOXYGEN__
+#define MEM_SIZE 1600
+#endif
+
+/**
+ * MEMP_OVERFLOW_CHECK: memp overflow protection reserves a configurable
+ * amount of bytes before and after each memp element in every pool and fills
+ * it with a prominent default value.
+ * MEMP_OVERFLOW_CHECK == 0 no checking
+ * MEMP_OVERFLOW_CHECK == 1 checks each element when it is freed
+ * MEMP_OVERFLOW_CHECK >= 2 checks each element in every pool every time
+ * memp_malloc() or memp_free() is called (useful but slow!)
+ */
+#if !defined MEMP_OVERFLOW_CHECK || defined __DOXYGEN__
+#define MEMP_OVERFLOW_CHECK 0
+#endif
+
+/**
+ * MEMP_SANITY_CHECK==1: run a sanity check after each memp_free() to make
+ * sure that there are no cycles in the linked lists.
+ */
+#if !defined MEMP_SANITY_CHECK || defined __DOXYGEN__
+#define MEMP_SANITY_CHECK 0
+#endif
+
+/**
+ * MEM_USE_POOLS==1: Use an alternative to malloc() by allocating from a set
+ * of memory pools of various sizes. When mem_malloc is called, an element of
+ * the smallest pool that can provide the length needed is returned.
+ * To use this, MEMP_USE_CUSTOM_POOLS also has to be enabled.
+ */
+#if !defined MEM_USE_POOLS || defined __DOXYGEN__
+#define MEM_USE_POOLS 0
+#endif
+
+/**
+ * MEM_USE_POOLS_TRY_BIGGER_POOL==1: if one malloc-pool is empty, try the next
+ * bigger pool - WARNING: THIS MIGHT WASTE MEMORY but it can make a system more
+ * reliable. */
+#if !defined MEM_USE_POOLS_TRY_BIGGER_POOL || defined __DOXYGEN__
+#define MEM_USE_POOLS_TRY_BIGGER_POOL 0
+#endif
+
+/**
+ * MEMP_USE_CUSTOM_POOLS==1: whether to include a user file lwippools.h
+ * that defines additional pools beyond the "standard" ones required
+ * by lwIP. If you set this to 1, you must have lwippools.h in your
+ * include path somewhere.
+ */
+#if !defined MEMP_USE_CUSTOM_POOLS || defined __DOXYGEN__
+#define MEMP_USE_CUSTOM_POOLS 0
+#endif
+
+/**
+ * Set this to 1 if you want to free PBUF_RAM pbufs (or call mem_free()) from
+ * interrupt context (or another context that doesn't allow waiting for a
+ * semaphore).
+ * If set to 1, mem_malloc will be protected by a semaphore and SYS_ARCH_PROTECT,
+ * while mem_free will only use SYS_ARCH_PROTECT. mem_malloc SYS_ARCH_UNPROTECTs
+ * with each loop so that mem_free can run.
+ *
+ * ATTENTION: As you can see from the above description, this leads to dis-/
+ * enabling interrupts often, which can be slow! Also, on low memory, mem_malloc
+ * can need longer.
+ *
+ * If you don't want that, at least for NO_SYS=0, you can still use the following
+ * functions to enqueue a deallocation call which then runs in the tcpip_thread
+ * context:
+ * - pbuf_free_callback(p);
+ * - mem_free_callback(m);
+ */
+#if !defined LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT || defined __DOXYGEN__
+#define LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT 0
+#endif
+/**
+ * @}
+ */
+
+/*
+ ------------------------------------------------
+ ---------- Internal Memory Pool Sizes ----------
+ ------------------------------------------------
+*/
+/**
+ * @defgroup lwip_opts_memp Internal memory pools
+ * @ingroup lwip_opts_infrastructure
+ * @{
+ */
+/**
+ * MEMP_NUM_PBUF: the number of memp struct pbufs (used for PBUF_ROM and PBUF_REF).
+ * If the application sends a lot of data out of ROM (or other static memory),
+ * this should be set high.
+ */
+#if !defined MEMP_NUM_PBUF || defined __DOXYGEN__
+#define MEMP_NUM_PBUF 16
+#endif
+
+/**
+ * MEMP_NUM_RAW_PCB: Number of raw connection PCBs
+ * (requires the LWIP_RAW option)
+ */
+#if !defined MEMP_NUM_RAW_PCB || defined __DOXYGEN__
+#define MEMP_NUM_RAW_PCB 4
+#endif
+
+/**
+ * MEMP_NUM_UDP_PCB: the number of UDP protocol control blocks. One
+ * per active UDP "connection".
+ * (requires the LWIP_UDP option)
+ */
+#if !defined MEMP_NUM_UDP_PCB || defined __DOXYGEN__
+#define MEMP_NUM_UDP_PCB 4
+#endif
+
+/**
+ * MEMP_NUM_TCP_PCB: the number of simultaneously active TCP connections.
+ * (requires the LWIP_TCP option)
+ */
+#if !defined MEMP_NUM_TCP_PCB || defined __DOXYGEN__
+#define MEMP_NUM_TCP_PCB 5
+#endif
+
+/**
+ * MEMP_NUM_TCP_PCB_LISTEN: the number of listening TCP connections.
+ * (requires the LWIP_TCP option)
+ */
+#if !defined MEMP_NUM_TCP_PCB_LISTEN || defined __DOXYGEN__
+#define MEMP_NUM_TCP_PCB_LISTEN 8
+#endif
+
+/**
+ * MEMP_NUM_TCP_SEG: the number of simultaneously queued TCP segments.
+ * (requires the LWIP_TCP option)
+ */
+#if !defined MEMP_NUM_TCP_SEG || defined __DOXYGEN__
+#define MEMP_NUM_TCP_SEG 16
+#endif
+
+/**
+ * MEMP_NUM_REASSDATA: the number of IP packets simultaneously queued for
+ * reassembly (whole packets, not fragments!)
+ */
+#if !defined MEMP_NUM_REASSDATA || defined __DOXYGEN__
+#define MEMP_NUM_REASSDATA 5
+#endif
+
+/**
+ * MEMP_NUM_FRAG_PBUF: the number of IP fragments simultaneously sent
+ * (fragments, not whole packets!).
+ * This is only used with LWIP_NETIF_TX_SINGLE_PBUF==0 and only has to be > 1
+ * with DMA-enabled MACs where the packet is not yet sent when netif->output
+ * returns.
+ */
+#if !defined MEMP_NUM_FRAG_PBUF || defined __DOXYGEN__
+#define MEMP_NUM_FRAG_PBUF 15
+#endif
+
+/**
+ * MEMP_NUM_ARP_QUEUE: the number of simultaneously queued outgoing
+ * packets (pbufs) that are waiting for an ARP request (to resolve
+ * their destination address) to finish.
+ * (requires the ARP_QUEUEING option)
+ */
+#if !defined MEMP_NUM_ARP_QUEUE || defined __DOXYGEN__
+#define MEMP_NUM_ARP_QUEUE 30
+#endif
+
+/**
+ * MEMP_NUM_IGMP_GROUP: The number of multicast groups whose network interfaces
+ * can be members at the same time (one per netif - allsystems group -, plus one
+ * per netif membership).
+ * (requires the LWIP_IGMP option)
+ */
+#if !defined MEMP_NUM_IGMP_GROUP || defined __DOXYGEN__
+#define MEMP_NUM_IGMP_GROUP 8
+#endif
+
+/**
+ * MEMP_NUM_SYS_TIMEOUT: the number of simultaneously active timeouts.
+ * The default number of timeouts is calculated here for all enabled modules.
+ * The formula expects settings to be either '0' or '1'.
+ */
+#if !defined MEMP_NUM_SYS_TIMEOUT || defined __DOXYGEN__
+#define MEMP_NUM_SYS_TIMEOUT (LWIP_TCP + IP_REASSEMBLY + LWIP_ARP + (2*LWIP_DHCP) + LWIP_AUTOIP + LWIP_IGMP + LWIP_DNS + (PPP_SUPPORT*6*MEMP_NUM_PPP_PCB) + (LWIP_IPV6 ? (1 + LWIP_IPV6_REASS + LWIP_IPV6_MLD) : 0))
+#endif
+
+/**
+ * MEMP_NUM_NETBUF: the number of struct netbufs.
+ * (only needed if you use the sequential API, like api_lib.c)
+ */
+#if !defined MEMP_NUM_NETBUF || defined __DOXYGEN__
+#define MEMP_NUM_NETBUF 2
+#endif
+
+/**
+ * MEMP_NUM_NETCONN: the number of struct netconns.
+ * (only needed if you use the sequential API, like api_lib.c)
+ */
+#if !defined MEMP_NUM_NETCONN || defined __DOXYGEN__
+#define MEMP_NUM_NETCONN 4
+#endif
+
+/**
+ * MEMP_NUM_TCPIP_MSG_API: the number of struct tcpip_msg, which are used
+ * for callback/timeout API communication.
+ * (only needed if you use tcpip.c)
+ */
+#if !defined MEMP_NUM_TCPIP_MSG_API || defined __DOXYGEN__
+#define MEMP_NUM_TCPIP_MSG_API 8
+#endif
+
+/**
+ * MEMP_NUM_TCPIP_MSG_INPKT: the number of struct tcpip_msg, which are used
+ * for incoming packets.
+ * (only needed if you use tcpip.c)
+ */
+#if !defined MEMP_NUM_TCPIP_MSG_INPKT || defined __DOXYGEN__
+#define MEMP_NUM_TCPIP_MSG_INPKT 8
+#endif
+
+/**
+ * MEMP_NUM_NETDB: the number of concurrently running lwip_addrinfo() calls
+ * (before freeing the corresponding memory using lwip_freeaddrinfo()).
+ */
+#if !defined MEMP_NUM_NETDB || defined __DOXYGEN__
+#define MEMP_NUM_NETDB 1
+#endif
+
+/**
+ * MEMP_NUM_LOCALHOSTLIST: the number of host entries in the local host list
+ * if DNS_LOCAL_HOSTLIST_IS_DYNAMIC==1.
+ */
+#if !defined MEMP_NUM_LOCALHOSTLIST || defined __DOXYGEN__
+#define MEMP_NUM_LOCALHOSTLIST 1
+#endif
+
+/**
+ * PBUF_POOL_SIZE: the number of buffers in the pbuf pool.
+ */
+#if !defined PBUF_POOL_SIZE || defined __DOXYGEN__
+#define PBUF_POOL_SIZE 16
+#endif
+
+/** MEMP_NUM_API_MSG: the number of concurrently active calls to various
+ * socket, netconn, and tcpip functions
+ */
+#if !defined MEMP_NUM_API_MSG || defined __DOXYGEN__
+#define MEMP_NUM_API_MSG MEMP_NUM_TCPIP_MSG_API
+#endif
+
+/** MEMP_NUM_DNS_API_MSG: the number of concurrently active calls to netconn_gethostbyname
+ */
+#if !defined MEMP_NUM_DNS_API_MSG || defined __DOXYGEN__
+#define MEMP_NUM_DNS_API_MSG MEMP_NUM_TCPIP_MSG_API
+#endif
+
+/** MEMP_NUM_SOCKET_SETGETSOCKOPT_DATA: the number of concurrently active calls
+ * to getsockopt/setsockopt
+ */
+#if !defined MEMP_NUM_SOCKET_SETGETSOCKOPT_DATA || defined __DOXYGEN__
+#define MEMP_NUM_SOCKET_SETGETSOCKOPT_DATA MEMP_NUM_TCPIP_MSG_API
+#endif
+
+/** MEMP_NUM_NETIFAPI_MSG: the number of concurrently active calls to the
+ * netifapi functions
+ */
+#if !defined MEMP_NUM_NETIFAPI_MSG || defined __DOXYGEN__
+#define MEMP_NUM_NETIFAPI_MSG MEMP_NUM_TCPIP_MSG_API
+#endif
+/**
+ * @}
+ */
+
+/*
+ ---------------------------------
+ ---------- ARP options ----------
+ ---------------------------------
+*/
+/**
+ * @defgroup lwip_opts_arp ARP
+ * @ingroup lwip_opts_ipv4
+ * @{
+ */
+/**
+ * LWIP_ARP==1: Enable ARP functionality.
+ */
+#if !defined LWIP_ARP || defined __DOXYGEN__
+#define LWIP_ARP 1
+#endif
+
+/**
+ * ARP_TABLE_SIZE: Number of active MAC-IP address pairs cached.
+ */
+#if !defined ARP_TABLE_SIZE || defined __DOXYGEN__
+#define ARP_TABLE_SIZE 10
+#endif
+
+/** the time an ARP entry stays valid after its last update,
+ * for ARP_TMR_INTERVAL = 1000, this is
+ * (60 * 5) seconds = 5 minutes.
+ */
+#if !defined ARP_MAXAGE || defined __DOXYGEN__
+#define ARP_MAXAGE 300
+#endif
+
+/**
+ * ARP_QUEUEING==1: Multiple outgoing packets are queued during hardware address
+ * resolution. By default, only the most recent packet is queued per IP address.
+ * This is sufficient for most protocols and mainly reduces TCP connection
+ * startup time. Set this to 1 if you know your application sends more than one
+ * packet in a row to an IP address that is not in the ARP cache.
+ */
+#if !defined ARP_QUEUEING || defined __DOXYGEN__
+#define ARP_QUEUEING 0
+#endif
+
+/** The maximum number of packets which may be queued for each
+ * unresolved address by other network layers. Defaults to 3, 0 means disabled.
+ * Old packets are dropped, new packets are queued.
+ */
+#if !defined ARP_QUEUE_LEN || defined __DOXYGEN__
+#define ARP_QUEUE_LEN 3
+#endif
+
+/**
+ * ETHARP_SUPPORT_VLAN==1: support receiving and sending ethernet packets with
+ * VLAN header. See the description of LWIP_HOOK_VLAN_CHECK and
+ * LWIP_HOOK_VLAN_SET hooks to check/set VLAN headers.
+ * 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.
+ */
+#if !defined ETHARP_SUPPORT_VLAN || defined __DOXYGEN__
+#define ETHARP_SUPPORT_VLAN 0
+#endif
+
+/** LWIP_ETHERNET==1: enable ethernet support even though ARP might be disabled
+ */
+#if !defined LWIP_ETHERNET || defined __DOXYGEN__
+#define LWIP_ETHERNET LWIP_ARP
+#endif
+
+/** ETH_PAD_SIZE: number of bytes added before the ethernet header to ensure
+ * alignment of payload after that header. Since the header is 14 bytes long,
+ * without this padding e.g. addresses in the IP header will not be aligned
+ * on a 32-bit boundary, so setting this to 2 can speed up 32-bit-platforms.
+ */
+#if !defined ETH_PAD_SIZE || defined __DOXYGEN__
+#define ETH_PAD_SIZE 0
+#endif
+
+/** ETHARP_SUPPORT_STATIC_ENTRIES==1: enable code to support static ARP table
+ * entries (using etharp_add_static_entry/etharp_remove_static_entry).
+ */
+#if !defined ETHARP_SUPPORT_STATIC_ENTRIES || defined __DOXYGEN__
+#define ETHARP_SUPPORT_STATIC_ENTRIES 0
+#endif
+
+/** ETHARP_TABLE_MATCH_NETIF==1: Match netif for ARP table entries.
+ * If disabled, duplicate IP address on multiple netifs are not supported
+ * (but this should only occur for AutoIP).
+ */
+#if !defined ETHARP_TABLE_MATCH_NETIF || defined __DOXYGEN__
+#define ETHARP_TABLE_MATCH_NETIF 0
+#endif
+/**
+ * @}
+ */
+
+/*
+ --------------------------------
+ ---------- IP options ----------
+ --------------------------------
+*/
+/**
+ * @defgroup lwip_opts_ipv4 IPv4
+ * @ingroup lwip_opts
+ * @{
+ */
+/**
+ * LWIP_IPV4==1: Enable IPv4
+ */
+#if !defined LWIP_IPV4 || defined __DOXYGEN__
+#define LWIP_IPV4 1
+#endif
+
+/**
+ * IP_FORWARD==1: Enables the ability to forward IP packets across network
+ * interfaces. If you are going to run lwIP on a device with only one network
+ * interface, define this to 0.
+ */
+#if !defined IP_FORWARD || defined __DOXYGEN__
+#define IP_FORWARD 0
+#endif
+
+/**
+ * IP_REASSEMBLY==1: Reassemble incoming fragmented IP packets. Note that
+ * this option does not affect outgoing packet sizes, which can be controlled
+ * via IP_FRAG.
+ */
+#if !defined IP_REASSEMBLY || defined __DOXYGEN__
+#define IP_REASSEMBLY 1
+#endif
+
+/**
+ * IP_FRAG==1: Fragment outgoing IP packets if their size exceeds MTU. Note
+ * that this option does not affect incoming packet sizes, which can be
+ * controlled via IP_REASSEMBLY.
+ */
+#if !defined IP_FRAG || defined __DOXYGEN__
+#define IP_FRAG 1
+#endif
+
+#if !LWIP_IPV4
+/* disable IPv4 extensions when IPv4 is disabled */
+#undef IP_FORWARD
+#define IP_FORWARD 0
+#undef IP_REASSEMBLY
+#define IP_REASSEMBLY 0
+#undef IP_FRAG
+#define IP_FRAG 0
+#endif /* !LWIP_IPV4 */
+
+/**
+ * IP_OPTIONS_ALLOWED: Defines the behavior for IP options.
+ * IP_OPTIONS_ALLOWED==0: All packets with IP options are dropped.
+ * IP_OPTIONS_ALLOWED==1: IP options are allowed (but not parsed).
+ */
+#if !defined IP_OPTIONS_ALLOWED || defined __DOXYGEN__
+#define IP_OPTIONS_ALLOWED 1
+#endif
+
+/**
+ * IP_REASS_MAXAGE: Maximum time (in multiples of IP_TMR_INTERVAL - so seconds, normally)
+ * a fragmented IP packet waits for all fragments to arrive. If not all fragments arrived
+ * in this time, the whole packet is discarded.
+ */
+#if !defined IP_REASS_MAXAGE || defined __DOXYGEN__
+#define IP_REASS_MAXAGE 3
+#endif
+
+/**
+ * IP_REASS_MAX_PBUFS: Total maximum amount of pbufs waiting to be reassembled.
+ * Since the received pbufs are enqueued, be sure to configure
+ * PBUF_POOL_SIZE > IP_REASS_MAX_PBUFS so that the stack is still able to receive
+ * packets even if the maximum amount of fragments is enqueued for reassembly!
+ */
+#if !defined IP_REASS_MAX_PBUFS || defined __DOXYGEN__
+#define IP_REASS_MAX_PBUFS 10
+#endif
+
+/**
+ * IP_DEFAULT_TTL: Default value for Time-To-Live used by transport layers.
+ */
+#if !defined IP_DEFAULT_TTL || defined __DOXYGEN__
+#define IP_DEFAULT_TTL 255
+#endif
+
+/**
+ * IP_SOF_BROADCAST=1: Use the SOF_BROADCAST field to enable broadcast
+ * filter per pcb on udp and raw send operations. To enable broadcast filter
+ * on recv operations, you also have to set IP_SOF_BROADCAST_RECV=1.
+ */
+#if !defined IP_SOF_BROADCAST || defined __DOXYGEN__
+#define IP_SOF_BROADCAST 0
+#endif
+
+/**
+ * IP_SOF_BROADCAST_RECV (requires IP_SOF_BROADCAST=1) enable the broadcast
+ * filter on recv operations.
+ */
+#if !defined IP_SOF_BROADCAST_RECV || defined __DOXYGEN__
+#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!
+ */
+#if !defined IP_FORWARD_ALLOW_TX_ON_RX_NETIF || defined __DOXYGEN__
+#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.
+ */
+#if !defined LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS || defined __DOXYGEN__
+#define LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS 0
+#endif
+/**
+ * @}
+ */
+
+/*
+ ----------------------------------
+ ---------- ICMP options ----------
+ ----------------------------------
+*/
+/**
+ * @defgroup lwip_opts_icmp ICMP
+ * @ingroup lwip_opts_ipv4
+ * @{
+ */
+/**
+ * LWIP_ICMP==1: Enable ICMP module inside the IP stack.
+ * Be careful, disable that make your product non-compliant to RFC1122
+ */
+#if !defined LWIP_ICMP || defined __DOXYGEN__
+#define LWIP_ICMP 1
+#endif
+
+/**
+ * ICMP_TTL: Default value for Time-To-Live used by ICMP packets.
+ */
+#if !defined ICMP_TTL || defined __DOXYGEN__
+#define ICMP_TTL (IP_DEFAULT_TTL)
+#endif
+
+/**
+ * LWIP_BROADCAST_PING==1: respond to broadcast pings (default is unicast only)
+ */
+#if !defined LWIP_BROADCAST_PING || defined __DOXYGEN__
+#define LWIP_BROADCAST_PING 0
+#endif
+
+/**
+ * LWIP_MULTICAST_PING==1: respond to multicast pings (default is unicast only)
+ */
+#if !defined LWIP_MULTICAST_PING || defined __DOXYGEN__
+#define LWIP_MULTICAST_PING 0
+#endif
+/**
+ * @}
+ */
+
+/*
+ ---------------------------------
+ ---------- RAW options ----------
+ ---------------------------------
+*/
+/**
+ * @defgroup lwip_opts_raw RAW
+ * @ingroup lwip_opts_callback
+ * @{
+ */
+/**
+ * LWIP_RAW==1: Enable application layer to hook into the IP layer itself.
+ */
+#if !defined LWIP_RAW || defined __DOXYGEN__
+#define LWIP_RAW 0
+#endif
+
+/**
+ * LWIP_RAW==1: Enable application layer to hook into the IP layer itself.
+ */
+#if !defined RAW_TTL || defined __DOXYGEN__
+#define RAW_TTL (IP_DEFAULT_TTL)
+#endif
+/**
+ * @}
+ */
+
+/*
+ ----------------------------------
+ ---------- DHCP options ----------
+ ----------------------------------
+*/
+/**
+ * @defgroup lwip_opts_dhcp DHCP
+ * @ingroup lwip_opts_ipv4
+ * @{
+ */
+/**
+ * LWIP_DHCP==1: Enable DHCP module.
+ */
+#if !defined LWIP_DHCP || defined __DOXYGEN__
+#define LWIP_DHCP 0
+#endif
+#if !LWIP_IPV4
+/* disable DHCP when IPv4 is disabled */
+#undef LWIP_DHCP
+#define LWIP_DHCP 0
+#endif /* !LWIP_IPV4 */
+
+/**
+ * DHCP_DOES_ARP_CHECK==1: Do an ARP check on the offered address.
+ */
+#if !defined DHCP_DOES_ARP_CHECK || defined __DOXYGEN__
+#define DHCP_DOES_ARP_CHECK ((LWIP_DHCP) && (LWIP_ARP))
+#endif
+
+/**
+ * LWIP_DHCP_CHECK_LINK_UP==1: dhcp_start() only really starts if the netif has
+ * NETIF_FLAG_LINK_UP set in its flags. As this is only an optimization and
+ * netif drivers might not set this flag, the default is off. If enabled,
+ * netif_set_link_up() must be called to continue dhcp starting.
+ */
+#if !defined LWIP_DHCP_CHECK_LINK_UP
+#define LWIP_DHCP_CHECK_LINK_UP 0
+#endif
+
+/**
+ * LWIP_DHCP_BOOTP_FILE==1: Store offered_si_addr and boot_file_name.
+ */
+#if !defined LWIP_DHCP_BOOTP_FILE || defined __DOXYGEN__
+#define LWIP_DHCP_BOOTP_FILE 0
+#endif
+
+/**
+ * LWIP_DHCP_GETS_NTP==1: Request NTP servers with discover/select. For each
+ * response packet, an callback is called, which has to be provided by the port:
+ * void dhcp_set_ntp_servers(u8_t num_ntp_servers, ip_addr_t* ntp_server_addrs);
+*/
+#if !defined LWIP_DHCP_GET_NTP_SRV || defined __DOXYGEN__
+#define LWIP_DHCP_GET_NTP_SRV 0
+#endif
+
+/**
+ * The maximum of NTP servers requested
+ */
+#if !defined LWIP_DHCP_MAX_NTP_SERVERS || defined __DOXYGEN__
+#define LWIP_DHCP_MAX_NTP_SERVERS 1
+#endif
+
+/**
+ * LWIP_DHCP_MAX_DNS_SERVERS > 0: Request DNS servers with discover/select.
+ * DHCP servers received in the response are passed to DNS via @ref dns_setserver()
+ * (up to the maximum limit defined here).
+ */
+#if !defined LWIP_DHCP_MAX_DNS_SERVERS || defined __DOXYGEN__
+#define LWIP_DHCP_MAX_DNS_SERVERS DNS_MAX_SERVERS
+#endif
+/**
+ * @}
+ */
+
+/*
+ ------------------------------------
+ ---------- AUTOIP options ----------
+ ------------------------------------
+*/
+/**
+ * @defgroup lwip_opts_autoip AUTOIP
+ * @ingroup lwip_opts_ipv4
+ * @{
+ */
+/**
+ * LWIP_AUTOIP==1: Enable AUTOIP module.
+ */
+#if !defined LWIP_AUTOIP || defined __DOXYGEN__
+#define LWIP_AUTOIP 0
+#endif
+#if !LWIP_IPV4
+/* disable AUTOIP when IPv4 is disabled */
+#undef LWIP_AUTOIP
+#define LWIP_AUTOIP 0
+#endif /* !LWIP_IPV4 */
+
+/**
+ * LWIP_DHCP_AUTOIP_COOP==1: Allow DHCP and AUTOIP to be both enabled on
+ * the same interface at the same time.
+ */
+#if !defined LWIP_DHCP_AUTOIP_COOP || defined __DOXYGEN__
+#define LWIP_DHCP_AUTOIP_COOP 0
+#endif
+
+/**
+ * LWIP_DHCP_AUTOIP_COOP_TRIES: Set to the number of DHCP DISCOVER probes
+ * that should be sent before falling back on AUTOIP (the DHCP client keeps
+ * running in this case). This can be set as low as 1 to get an AutoIP address
+ * very quickly, but you should be prepared to handle a changing IP address
+ * when DHCP overrides AutoIP.
+ */
+#if !defined LWIP_DHCP_AUTOIP_COOP_TRIES || defined __DOXYGEN__
+#define LWIP_DHCP_AUTOIP_COOP_TRIES 9
+#endif
+/**
+ * @}
+ */
+
+/*
+ ----------------------------------
+ ----- SNMP MIB2 support -----
+ ----------------------------------
+*/
+/**
+ * @defgroup lwip_opts_mib2 SNMP MIB2 callbacks
+ * @ingroup lwip_opts_infrastructure
+ * @{
+ */
+/**
+ * LWIP_MIB2_CALLBACKS==1: Turn on SNMP MIB2 callbacks.
+ * Turn this on to get callbacks needed to implement MIB2.
+ * Usually MIB2_STATS should be enabled, too.
+ */
+#if !defined LWIP_MIB2_CALLBACKS || defined __DOXYGEN__
+#define LWIP_MIB2_CALLBACKS 0
+#endif
+/**
+ * @}
+ */
+
+/*
+ ----------------------------------
+ ----- Multicast/IGMP options -----
+ ----------------------------------
+*/
+/**
+ * @defgroup lwip_opts_igmp IGMP
+ * @ingroup lwip_opts_ipv4
+ * @{
+ */
+/**
+ * LWIP_IGMP==1: Turn on IGMP module.
+ */
+#if !defined LWIP_IGMP || defined __DOXYGEN__
+#define LWIP_IGMP 0
+#endif
+#if !LWIP_IPV4
+#undef LWIP_IGMP
+#define LWIP_IGMP 0
+#endif
+
+/**
+ * LWIP_MULTICAST_TX_OPTIONS==1: Enable multicast TX support like the socket options
+ * IP_MULTICAST_TTL/IP_MULTICAST_IF/IP_MULTICAST_LOOP
+ */
+#if !defined LWIP_MULTICAST_TX_OPTIONS || defined __DOXYGEN__
+#define LWIP_MULTICAST_TX_OPTIONS (LWIP_IGMP && LWIP_UDP)
+#endif
+/**
+ * @}
+ */
+
+/*
+ ----------------------------------
+ ---------- DNS options -----------
+ ----------------------------------
+*/
+/**
+ * @defgroup lwip_opts_dns DNS
+ * @ingroup lwip_opts_callback
+ * @{
+ */
+/**
+ * LWIP_DNS==1: Turn on DNS module. UDP must be available for DNS
+ * transport.
+ */
+#if !defined LWIP_DNS || defined __DOXYGEN__
+#define LWIP_DNS 0
+#endif
+
+/** DNS maximum number of entries to maintain locally. */
+#if !defined DNS_TABLE_SIZE || defined __DOXYGEN__
+#define DNS_TABLE_SIZE 4
+#endif
+
+/** DNS maximum host name length supported in the name table. */
+#if !defined DNS_MAX_NAME_LENGTH || defined __DOXYGEN__
+#define DNS_MAX_NAME_LENGTH 256
+#endif
+
+/** The maximum of DNS servers
+ * The first server can be initialized automatically by defining
+ * DNS_SERVER_ADDRESS(ipaddr), where 'ipaddr' is an 'ip_addr_t*'
+ */
+#if !defined DNS_MAX_SERVERS || defined __DOXYGEN__
+#define DNS_MAX_SERVERS 2
+#endif
+
+/** DNS do a name checking between the query and the response. */
+#if !defined DNS_DOES_NAME_CHECK || defined __DOXYGEN__
+#define DNS_DOES_NAME_CHECK 1
+#endif
+
+/** LWIP_DNS_SECURE: controls the security level of the DNS implementation
+ * Use all DNS security features by default.
+ * This is overridable but should only be needed by very small targets
+ * or when using against non standard DNS servers. */
+#if !defined LWIP_DNS_SECURE || defined __DOXYGEN__
+#define LWIP_DNS_SECURE (LWIP_DNS_SECURE_RAND_XID | LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING | LWIP_DNS_SECURE_RAND_SRC_PORT)
+#endif
+
+/* A list of DNS security features follows */
+#define LWIP_DNS_SECURE_RAND_XID 1
+#define LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING 2
+#define LWIP_DNS_SECURE_RAND_SRC_PORT 4
+
+/** DNS_LOCAL_HOSTLIST: Implements a local host-to-address list. If enabled, you have to define an initializer:
+ * \#define DNS_LOCAL_HOSTLIST_INIT {DNS_LOCAL_HOSTLIST_ELEM("host_ip4", IPADDR4_INIT_BYTES(1,2,3,4)), \
+ * DNS_LOCAL_HOSTLIST_ELEM("host_ip6", IPADDR6_INIT_HOST(123, 234, 345, 456)}
+ *
+ * Instead, you can also use an external function:
+ * \#define DNS_LOOKUP_LOCAL_EXTERN(x) extern err_t my_lookup_function(const char *name, ip_addr_t *addr, u8_t dns_addrtype)
+ * that looks up the IP address and returns ERR_OK if found (LWIP_DNS_ADDRTYPE_xxx is passed in dns_addrtype).
+ */
+#if !defined DNS_LOCAL_HOSTLIST || defined __DOXYGEN__
+#define DNS_LOCAL_HOSTLIST 0
+#endif /* DNS_LOCAL_HOSTLIST */
+
+/** If this is turned on, the local host-list can be dynamically changed
+ * at runtime. */
+#if !defined DNS_LOCAL_HOSTLIST_IS_DYNAMIC || defined __DOXYGEN__
+#define DNS_LOCAL_HOSTLIST_IS_DYNAMIC 0
+#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
+
+/** Set this to 1 to enable querying ".local" names via mDNS
+ * using a One-Shot Multicast DNS Query */
+#if !defined LWIP_DNS_SUPPORT_MDNS_QUERIES || defined __DOXYGEN__
+#define LWIP_DNS_SUPPORT_MDNS_QUERIES 0
+#endif
+/**
+ * @}
+ */
+
+/*
+ ---------------------------------
+ ---------- UDP options ----------
+ ---------------------------------
+*/
+/**
+ * @defgroup lwip_opts_udp UDP
+ * @ingroup lwip_opts_callback
+ * @{
+ */
+/**
+ * LWIP_UDP==1: Turn on UDP.
+ */
+#if !defined LWIP_UDP || defined __DOXYGEN__
+#define LWIP_UDP 1
+#endif
+
+/**
+ * LWIP_UDPLITE==1: Turn on UDP-Lite. (Requires LWIP_UDP)
+ */
+#if !defined LWIP_UDPLITE || defined __DOXYGEN__
+#define LWIP_UDPLITE 0
+#endif
+
+/**
+ * UDP_TTL: Default Time-To-Live value.
+ */
+#if !defined UDP_TTL || defined __DOXYGEN__
+#define UDP_TTL (IP_DEFAULT_TTL)
+#endif
+
+/**
+ * LWIP_NETBUF_RECVINFO==1: append destination addr and port to every netbuf.
+ */
+#if !defined LWIP_NETBUF_RECVINFO || defined __DOXYGEN__
+#define LWIP_NETBUF_RECVINFO 0
+#endif
+/**
+ * @}
+ */
+
+/*
+ ---------------------------------
+ ---------- TCP options ----------
+ ---------------------------------
+*/
+/**
+ * @defgroup lwip_opts_tcp TCP
+ * @ingroup lwip_opts_callback
+ * @{
+ */
+/**
+ * LWIP_TCP==1: Turn on TCP.
+ */
+#if !defined LWIP_TCP || defined __DOXYGEN__
+#define LWIP_TCP 1
+#endif
+
+/**
+ * TCP_TTL: Default Time-To-Live value.
+ */
+#if !defined TCP_TTL || defined __DOXYGEN__
+#define TCP_TTL (IP_DEFAULT_TTL)
+#endif
+
+/**
+ * TCP_WND: The size of a TCP window. This must be at least
+ * (2 * TCP_MSS) for things to work well.
+ * ATTENTION: when using TCP_RCV_SCALE, TCP_WND is the total size
+ * with scaling applied. Maximum window value in the TCP header
+ * will be TCP_WND >> TCP_RCV_SCALE
+ */
+#if !defined TCP_WND || defined __DOXYGEN__
+#define TCP_WND (4 * TCP_MSS)
+#endif
+
+/**
+ * TCP_MAXRTX: Maximum number of retransmissions of data segments.
+ */
+#if !defined TCP_MAXRTX || defined __DOXYGEN__
+#define TCP_MAXRTX 12
+#endif
+
+/**
+ * TCP_SYNMAXRTX: Maximum number of retransmissions of SYN segments.
+ */
+#if !defined TCP_SYNMAXRTX || defined __DOXYGEN__
+#define TCP_SYNMAXRTX 6
+#endif
+
+/**
+ * TCP_QUEUE_OOSEQ==1: TCP will queue segments that arrive out of order.
+ * Define to 0 if your device is low on memory.
+ */
+#if !defined TCP_QUEUE_OOSEQ || defined __DOXYGEN__
+#define TCP_QUEUE_OOSEQ (LWIP_TCP)
+#endif
+
+/**
+ * TCP_MSS: TCP Maximum segment size. (default is 536, a conservative default,
+ * you might want to increase this.)
+ * For the receive side, this MSS is advertised to the remote side
+ * when opening a connection. For the transmit size, this MSS sets
+ * an upper limit on the MSS advertised by the remote host.
+ */
+#if !defined TCP_MSS || defined __DOXYGEN__
+#define TCP_MSS 536
+#endif
+
+/**
+ * TCP_CALCULATE_EFF_SEND_MSS: "The maximum size of a segment that TCP really
+ * sends, the 'effective send MSS,' MUST be the smaller of the send MSS (which
+ * reflects the available reassembly buffer size at the remote host) and the
+ * largest size permitted by the IP layer" (RFC 1122)
+ * Setting this to 1 enables code that checks TCP_MSS against the MTU of the
+ * netif used for a connection and limits the MSS if it would be too big otherwise.
+ */
+#if !defined TCP_CALCULATE_EFF_SEND_MSS || defined __DOXYGEN__
+#define TCP_CALCULATE_EFF_SEND_MSS 1
+#endif
+
+
+/**
+ * TCP_SND_BUF: TCP sender buffer space (bytes).
+ * To achieve good performance, this should be at least 2 * TCP_MSS.
+ */
+#if !defined TCP_SND_BUF || defined __DOXYGEN__
+#define TCP_SND_BUF (2 * TCP_MSS)
+#endif
+
+/**
+ * TCP_SND_QUEUELEN: TCP sender buffer space (pbufs). This must be at least
+ * as much as (2 * TCP_SND_BUF/TCP_MSS) for things to work.
+ */
+#if !defined TCP_SND_QUEUELEN || defined __DOXYGEN__
+#define TCP_SND_QUEUELEN ((4 * (TCP_SND_BUF) + (TCP_MSS - 1))/(TCP_MSS))
+#endif
+
+/**
+ * TCP_SNDLOWAT: TCP writable space (bytes). This must be less than
+ * TCP_SND_BUF. It is the amount of space which must be available in the
+ * TCP snd_buf for select to return writable (combined with TCP_SNDQUEUELOWAT).
+ */
+#if !defined TCP_SNDLOWAT || defined __DOXYGEN__
+#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 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).
+ */
+#if !defined TCP_SNDQUEUELOWAT || defined __DOXYGEN__
+#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==1.
+ */
+#if !defined TCP_OOSEQ_MAX_BYTES || defined __DOXYGEN__
+#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==1.
+ */
+#if !defined TCP_OOSEQ_MAX_PBUFS || defined __DOXYGEN__
+#define TCP_OOSEQ_MAX_PBUFS 0
+#endif
+
+/**
+ * TCP_LISTEN_BACKLOG: Enable the backlog option for tcp listen pcb.
+ */
+#if !defined TCP_LISTEN_BACKLOG || defined __DOXYGEN__
+#define TCP_LISTEN_BACKLOG 0
+#endif
+
+/**
+ * The maximum allowed backlog for TCP listen netconns.
+ * This backlog is used unless another is explicitly specified.
+ * 0xff is the maximum (u8_t).
+ */
+#if !defined TCP_DEFAULT_LISTEN_BACKLOG || defined __DOXYGEN__
+#define TCP_DEFAULT_LISTEN_BACKLOG 0xff
+#endif
+
+/**
+ * TCP_OVERSIZE: The maximum number of bytes that tcp_write may
+ * allocate ahead of time in an attempt to create shorter pbuf chains
+ * for transmission. The meaningful range is 0 to TCP_MSS. Some
+ * suggested values are:
+ *
+ * 0: Disable oversized allocation. Each tcp_write() allocates a new
+ pbuf (old behaviour).
+ * 1: Allocate size-aligned pbufs with minimal excess. Use this if your
+ * scatter-gather DMA requires aligned fragments.
+ * 128: Limit the pbuf/memory overhead to 20%.
+ * TCP_MSS: Try to create unfragmented TCP packets.
+ * TCP_MSS/4: Try to create 4 fragments or less per TCP packet.
+ */
+#if !defined TCP_OVERSIZE || defined __DOXYGEN__
+#define TCP_OVERSIZE TCP_MSS
+#endif
+
+/**
+ * LWIP_TCP_TIMESTAMPS==1: support the TCP timestamp option.
+ * The timestamp option is currently only used to help remote hosts, it is not
+ * really used locally. Therefore, it is only enabled when a TS option is
+ * received in the initial SYN packet from a remote host.
+ */
+#if !defined LWIP_TCP_TIMESTAMPS || defined __DOXYGEN__
+#define LWIP_TCP_TIMESTAMPS 0
+#endif
+
+/**
+ * TCP_WND_UPDATE_THRESHOLD: difference in window to trigger an
+ * explicit window update
+ */
+#if !defined TCP_WND_UPDATE_THRESHOLD || defined __DOXYGEN__
+#define TCP_WND_UPDATE_THRESHOLD LWIP_MIN((TCP_WND / 4), (TCP_MSS * 4))
+#endif
+
+/**
+ * LWIP_EVENT_API and LWIP_CALLBACK_API: Only one of these should be set to 1.
+ * LWIP_EVENT_API==1: The user defines lwip_tcp_event() to receive all
+ * events (accept, sent, etc) that happen in the system.
+ * LWIP_CALLBACK_API==1: The PCB callback function is called directly
+ * for the event. This is the default.
+ */
+#if !defined(LWIP_EVENT_API) && !defined(LWIP_CALLBACK_API) || defined __DOXYGEN__
+#define LWIP_EVENT_API 0
+#define LWIP_CALLBACK_API 1
+#else
+#ifndef LWIP_EVENT_API
+#define LWIP_EVENT_API 0
+#endif
+#ifndef LWIP_CALLBACK_API
+#define LWIP_CALLBACK_API 0
+#endif
+#endif
+
+/**
+ * LWIP_WND_SCALE and TCP_RCV_SCALE:
+ * Set LWIP_WND_SCALE to 1 to enable window scaling.
+ * Set TCP_RCV_SCALE to the desired scaling factor (shift count in the
+ * range of [0..14]).
+ * When LWIP_WND_SCALE is enabled but TCP_RCV_SCALE is 0, we can use a large
+ * send window while having a small receive window only.
+ */
+#if !defined LWIP_WND_SCALE || defined __DOXYGEN__
+#define LWIP_WND_SCALE 0
+#define TCP_RCV_SCALE 0
+#endif
+/**
+ * @}
+ */
+
+/*
+ ----------------------------------
+ ---------- Pbuf options ----------
+ ----------------------------------
+*/
+/**
+ * @defgroup lwip_opts_pbuf PBUF
+ * @ingroup lwip_opts
+ * @{
+ */
+/**
+ * PBUF_LINK_HLEN: the number of bytes that should be allocated for a
+ * link level header. The default is 14, the standard value for
+ * Ethernet.
+ */
+#if !defined PBUF_LINK_HLEN || defined __DOXYGEN__
+#if defined LWIP_HOOK_VLAN_SET && !defined __DOXYGEN__
+#define PBUF_LINK_HLEN (18 + ETH_PAD_SIZE)
+#else /* LWIP_HOOK_VLAN_SET */
+#define PBUF_LINK_HLEN (14 + ETH_PAD_SIZE)
+#endif /* LWIP_HOOK_VLAN_SET */
+#endif
+
+/**
+ * PBUF_LINK_ENCAPSULATION_HLEN: the number of bytes that should be allocated
+ * for an additional encapsulation header before ethernet headers (e.g. 802.11)
+ */
+#if !defined PBUF_LINK_ENCAPSULATION_HLEN || defined __DOXYGEN__
+#define PBUF_LINK_ENCAPSULATION_HLEN 0u
+#endif
+
+/**
+ * PBUF_POOL_BUFSIZE: the size of each pbuf in the pbuf pool. The default is
+ * designed to accommodate single full size TCP frame in one pbuf, including
+ * TCP_MSS, IP header, and link header.
+ */
+#if !defined PBUF_POOL_BUFSIZE || defined __DOXYGEN__
+#define PBUF_POOL_BUFSIZE LWIP_MEM_ALIGN_SIZE(TCP_MSS+40+PBUF_LINK_ENCAPSULATION_HLEN+PBUF_LINK_HLEN)
+#endif
+/**
+ * @}
+ */
+
+/*
+ ------------------------------------------------
+ ---------- Network Interfaces options ----------
+ ------------------------------------------------
+*/
+/**
+ * @defgroup lwip_opts_netif NETIF
+ * @ingroup lwip_opts
+ * @{
+ */
+/**
+ * LWIP_NETIF_HOSTNAME==1: use DHCP_OPTION_HOSTNAME with netif's hostname
+ * field.
+ */
+#if !defined LWIP_NETIF_HOSTNAME || defined __DOXYGEN__
+#define LWIP_NETIF_HOSTNAME 0
+#endif
+
+/**
+ * LWIP_NETIF_API==1: Support netif api (in netifapi.c)
+ */
+#if !defined LWIP_NETIF_API || defined __DOXYGEN__
+#define LWIP_NETIF_API 0
+#endif
+
+/**
+ * LWIP_NETIF_STATUS_CALLBACK==1: Support a callback function whenever an interface
+ * changes its up/down status (i.e., due to DHCP IP acquisition)
+ */
+#if !defined LWIP_NETIF_STATUS_CALLBACK || defined __DOXYGEN__
+#define LWIP_NETIF_STATUS_CALLBACK 0
+#endif
+
+/**
+ * LWIP_NETIF_LINK_CALLBACK==1: Support a callback function from an interface
+ * whenever the link changes (i.e., link down)
+ */
+#if !defined LWIP_NETIF_LINK_CALLBACK || defined __DOXYGEN__
+#define LWIP_NETIF_LINK_CALLBACK 0
+#endif
+
+/**
+ * LWIP_NETIF_REMOVE_CALLBACK==1: Support a callback function that is called
+ * when a netif has been removed
+ */
+#if !defined LWIP_NETIF_REMOVE_CALLBACK || defined __DOXYGEN__
+#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
+ * ARP tables or many concurrent connections, it might be counterproductive
+ * if you have a tiny ARP table or if there never are concurrent connections.
+ */
+#if !defined LWIP_NETIF_HWADDRHINT || defined __DOXYGEN__
+#define LWIP_NETIF_HWADDRHINT 0
+#endif
+
+/**
+ * LWIP_NETIF_TX_SINGLE_PBUF: if this is set to 1, lwIP tries to put all data
+ * to be sent into one single pbuf. This is for compatibility with DMA-enabled
+ * MACs that do not support scatter-gather.
+ * Beware that this might involve CPU-memcpy before transmitting that would not
+ * be needed without this flag! Use this only if you need to!
+ *
+ * @todo: TCP and IP-frag do not work with this, yet:
+ */
+#if !defined LWIP_NETIF_TX_SINGLE_PBUF || defined __DOXYGEN__
+#define LWIP_NETIF_TX_SINGLE_PBUF 0
+#endif /* LWIP_NETIF_TX_SINGLE_PBUF */
+
+/**
+ * LWIP_NUM_NETIF_CLIENT_DATA: Number of clients that may store
+ * data in client_data member array of struct netif.
+ */
+#if !defined LWIP_NUM_NETIF_CLIENT_DATA || defined __DOXYGEN__
+#define LWIP_NUM_NETIF_CLIENT_DATA 0
+#endif
+/**
+ * @}
+ */
+
+/*
+ ------------------------------------
+ ---------- LOOPIF options ----------
+ ------------------------------------
+*/
+/**
+ * @defgroup lwip_opts_loop Loopback interface
+ * @ingroup lwip_opts_netif
+ * @{
+ */
+/**
+ * LWIP_HAVE_LOOPIF==1: Support loop interface (127.0.0.1).
+ * This is only needed when no real netifs are available. If at least one other
+ * netif is available, loopback traffic uses this netif.
+ */
+#if !defined LWIP_HAVE_LOOPIF || defined __DOXYGEN__
+#define LWIP_HAVE_LOOPIF LWIP_NETIF_LOOPBACK
+#endif
+
+/**
+ * LWIP_LOOPIF_MULTICAST==1: Support multicast/IGMP on loop interface (127.0.0.1).
+ */
+#if !defined LWIP_LOOPIF_MULTICAST || defined __DOXYGEN__
+#define LWIP_LOOPIF_MULTICAST 0
+#endif
+
+/**
+ * LWIP_NETIF_LOOPBACK==1: Support sending packets with a destination IP
+ * address equal to the netif IP address, looping them back up the stack.
+ */
+#if !defined LWIP_NETIF_LOOPBACK || defined __DOXYGEN__
+#define LWIP_NETIF_LOOPBACK 0
+#endif
+
+/**
+ * LWIP_LOOPBACK_MAX_PBUFS: Maximum number of pbufs on queue for loopback
+ * sending for each netif (0 = disabled)
+ */
+#if !defined LWIP_LOOPBACK_MAX_PBUFS || defined __DOXYGEN__
+#define LWIP_LOOPBACK_MAX_PBUFS 0
+#endif
+
+/**
+ * LWIP_NETIF_LOOPBACK_MULTITHREADING: Indicates whether threading is enabled in
+ * the system, as netifs must change how they behave depending on this setting
+ * for the LWIP_NETIF_LOOPBACK option to work.
+ * Setting this is needed to avoid reentering non-reentrant functions like
+ * tcp_input().
+ * LWIP_NETIF_LOOPBACK_MULTITHREADING==1: Indicates that the user is using a
+ * multithreaded environment like tcpip.c. In this case, netif->input()
+ * is called directly.
+ * LWIP_NETIF_LOOPBACK_MULTITHREADING==0: Indicates a polling (or NO_SYS) setup.
+ * The packets are put on a list and netif_poll() must be called in
+ * the main application loop.
+ */
+#if !defined LWIP_NETIF_LOOPBACK_MULTITHREADING || defined __DOXYGEN__
+#define LWIP_NETIF_LOOPBACK_MULTITHREADING (!NO_SYS)
+#endif
+/**
+ * @}
+ */
+
+/*
+ ------------------------------------
+ ---------- Thread options ----------
+ ------------------------------------
+*/
+/**
+ * @defgroup lwip_opts_thread Threading
+ * @ingroup lwip_opts_infrastructure
+ * @{
+ */
+/**
+ * TCPIP_THREAD_NAME: The name assigned to the main tcpip thread.
+ */
+#if !defined TCPIP_THREAD_NAME || defined __DOXYGEN__
+#define TCPIP_THREAD_NAME "tcpip_thread"
+#endif
+
+/**
+ * TCPIP_THREAD_STACKSIZE: The stack size used by the main tcpip thread.
+ * The stack size value itself is platform-dependent, but is passed to
+ * sys_thread_new() when the thread is created.
+ */
+#if !defined TCPIP_THREAD_STACKSIZE || defined __DOXYGEN__
+#define TCPIP_THREAD_STACKSIZE 0
+#endif
+
+/**
+ * TCPIP_THREAD_PRIO: The priority assigned to the main tcpip thread.
+ * The priority value itself is platform-dependent, but is passed to
+ * sys_thread_new() when the thread is created.
+ */
+#if !defined TCPIP_THREAD_PRIO || defined __DOXYGEN__
+#define TCPIP_THREAD_PRIO 1
+#endif
+
+/**
+ * TCPIP_MBOX_SIZE: The mailbox size for the tcpip thread messages
+ * The queue size value itself is platform-dependent, but is passed to
+ * sys_mbox_new() when tcpip_init is called.
+ */
+#if !defined TCPIP_MBOX_SIZE || defined __DOXYGEN__
+#define TCPIP_MBOX_SIZE 0
+#endif
+
+/**
+ * Define this to something that triggers a watchdog. This is called from
+ * tcpip_thread after processing a message.
+ */
+#if !defined LWIP_TCPIP_THREAD_ALIVE || defined __DOXYGEN__
+#define LWIP_TCPIP_THREAD_ALIVE()
+#endif
+
+/**
+ * SLIPIF_THREAD_NAME: The name assigned to the slipif_loop thread.
+ */
+#if !defined SLIPIF_THREAD_NAME || defined __DOXYGEN__
+#define SLIPIF_THREAD_NAME "slipif_loop"
+#endif
+
+/**
+ * SLIP_THREAD_STACKSIZE: The stack size used by the slipif_loop thread.
+ * The stack size value itself is platform-dependent, but is passed to
+ * sys_thread_new() when the thread is created.
+ */
+#if !defined SLIPIF_THREAD_STACKSIZE || defined __DOXYGEN__
+#define SLIPIF_THREAD_STACKSIZE 0
+#endif
+
+/**
+ * SLIPIF_THREAD_PRIO: The priority assigned to the slipif_loop thread.
+ * The priority value itself is platform-dependent, but is passed to
+ * sys_thread_new() when the thread is created.
+ */
+#if !defined SLIPIF_THREAD_PRIO || defined __DOXYGEN__
+#define SLIPIF_THREAD_PRIO 1
+#endif
+
+/**
+ * DEFAULT_THREAD_NAME: The name assigned to any other lwIP thread.
+ */
+#if !defined DEFAULT_THREAD_NAME || defined __DOXYGEN__
+#define DEFAULT_THREAD_NAME "lwIP"
+#endif
+
+/**
+ * DEFAULT_THREAD_STACKSIZE: The stack size used by any other lwIP thread.
+ * The stack size value itself is platform-dependent, but is passed to
+ * sys_thread_new() when the thread is created.
+ */
+#if !defined DEFAULT_THREAD_STACKSIZE || defined __DOXYGEN__
+#define DEFAULT_THREAD_STACKSIZE 0
+#endif
+
+/**
+ * DEFAULT_THREAD_PRIO: The priority assigned to any other lwIP thread.
+ * The priority value itself is platform-dependent, but is passed to
+ * sys_thread_new() when the thread is created.
+ */
+#if !defined DEFAULT_THREAD_PRIO || defined __DOXYGEN__
+#define DEFAULT_THREAD_PRIO 1
+#endif
+
+/**
+ * DEFAULT_RAW_RECVMBOX_SIZE: The mailbox size for the incoming packets on a
+ * NETCONN_RAW. The queue size value itself is platform-dependent, but is passed
+ * to sys_mbox_new() when the recvmbox is created.
+ */
+#if !defined DEFAULT_RAW_RECVMBOX_SIZE || defined __DOXYGEN__
+#define DEFAULT_RAW_RECVMBOX_SIZE 0
+#endif
+
+/**
+ * DEFAULT_UDP_RECVMBOX_SIZE: The mailbox size for the incoming packets on a
+ * NETCONN_UDP. The queue size value itself is platform-dependent, but is passed
+ * to sys_mbox_new() when the recvmbox is created.
+ */
+#if !defined DEFAULT_UDP_RECVMBOX_SIZE || defined __DOXYGEN__
+#define DEFAULT_UDP_RECVMBOX_SIZE 0
+#endif
+
+/**
+ * DEFAULT_TCP_RECVMBOX_SIZE: The mailbox size for the incoming packets on a
+ * NETCONN_TCP. The queue size value itself is platform-dependent, but is passed
+ * to sys_mbox_new() when the recvmbox is created.
+ */
+#if !defined DEFAULT_TCP_RECVMBOX_SIZE || defined __DOXYGEN__
+#define DEFAULT_TCP_RECVMBOX_SIZE 0
+#endif
+
+/**
+ * DEFAULT_ACCEPTMBOX_SIZE: The mailbox size for the incoming connections.
+ * The queue size value itself is platform-dependent, but is passed to
+ * sys_mbox_new() when the acceptmbox is created.
+ */
+#if !defined DEFAULT_ACCEPTMBOX_SIZE || defined __DOXYGEN__
+#define DEFAULT_ACCEPTMBOX_SIZE 0
+#endif
+/**
+ * @}
+ */
+
+/*
+ ----------------------------------------------
+ ---------- Sequential layer options ----------
+ ----------------------------------------------
+*/
+/**
+ * @defgroup lwip_opts_netconn Netconn
+ * @ingroup lwip_opts_threadsafe_apis
+ * @{
+ */
+/**
+ * LWIP_NETCONN==1: Enable Netconn API (require to use api_lib.c)
+ */
+#if !defined LWIP_NETCONN || defined __DOXYGEN__
+#define LWIP_NETCONN 1
+#endif
+
+/** LWIP_TCPIP_TIMEOUT==1: Enable tcpip_timeout/tcpip_untimeout to create
+ * timers running in tcpip_thread from another thread.
+ */
+#if !defined LWIP_TCPIP_TIMEOUT || defined __DOXYGEN__
+#define LWIP_TCPIP_TIMEOUT 0
+#endif
+
+/** LWIP_NETCONN_SEM_PER_THREAD==1: Use one (thread-local) semaphore per
+ * thread calling socket/netconn functions instead of allocating one
+ * semaphore per netconn (and per select etc.)
+ * ATTENTION: a thread-local semaphore for API calls is needed:
+ * - LWIP_NETCONN_THREAD_SEM_GET() returning a sys_sem_t*
+ * - LWIP_NETCONN_THREAD_SEM_ALLOC() creating the semaphore
+ * - LWIP_NETCONN_THREAD_SEM_FREE() freeing the semaphore
+ * The latter 2 can be invoked up by calling netconn_thread_init()/netconn_thread_cleanup().
+ * Ports may call these for threads created with sys_thread_new().
+ */
+#if !defined LWIP_NETCONN_SEM_PER_THREAD || defined __DOXYGEN__
+#define LWIP_NETCONN_SEM_PER_THREAD 0
+#endif
+
+/** LWIP_NETCONN_FULLDUPLEX==1: Enable code that allows reading from one thread,
+ * writing from a 2nd thread and closing from a 3rd thread at the same time.
+ * ATTENTION: This is currently really alpha! Some requirements:
+ * - LWIP_NETCONN_SEM_PER_THREAD==1 is required to use one socket/netconn from
+ * multiple threads at once
+ * - sys_mbox_free() has to unblock receive tasks waiting on recvmbox/acceptmbox
+ * and prevent a task pending on this during/after deletion
+ */
+#if !defined LWIP_NETCONN_FULLDUPLEX || defined __DOXYGEN__
+#define LWIP_NETCONN_FULLDUPLEX 0
+#endif
+/**
+ * @}
+ */
+
+/*
+ ------------------------------------
+ ---------- Socket options ----------
+ ------------------------------------
+*/
+/**
+ * @defgroup lwip_opts_socket Sockets
+ * @ingroup lwip_opts_threadsafe_apis
+ * @{
+ */
+/**
+ * LWIP_SOCKET==1: Enable Socket API (require to use sockets.c)
+ */
+#if !defined LWIP_SOCKET || defined __DOXYGEN__
+#define LWIP_SOCKET 1
+#endif
+
+/* LWIP_SOCKET_SET_ERRNO==1: Set errno when socket functions cannot complete
+ * successfully, as required by POSIX. Default is POSIX-compliant.
+ */
+#if !defined LWIP_SOCKET_SET_ERRNO || defined __DOXYGEN__
+#define LWIP_SOCKET_SET_ERRNO 1
+#endif
+
+/**
+ * LWIP_COMPAT_SOCKETS==1: Enable BSD-style sockets functions names through defines.
+ * LWIP_COMPAT_SOCKETS==2: Same as ==1 but correctly named functions are created.
+ * While this helps code completion, it might conflict with existing libraries.
+ * (only used if you use sockets.c)
+ */
+#if !defined LWIP_COMPAT_SOCKETS || defined __DOXYGEN__
+#define LWIP_COMPAT_SOCKETS 1
+#endif
+
+/**
+ * LWIP_POSIX_SOCKETS_IO_NAMES==1: Enable POSIX-style sockets functions names.
+ * Disable this option if you use a POSIX operating system that uses the same
+ * names (read, write & close). (only used if you use sockets.c)
+ */
+#if !defined LWIP_POSIX_SOCKETS_IO_NAMES || defined __DOXYGEN__
+#define LWIP_POSIX_SOCKETS_IO_NAMES 1
+#endif
+
+/**
+ * LWIP_SOCKET_OFFSET==n: Increases the file descriptor number created by LwIP with n.
+ * This can be useful when there are multiple APIs which create file descriptors.
+ * When they all start with a different offset and you won't make them overlap you can
+ * re implement read/write/close/ioctl/fnctl to send the requested action to the right
+ * library (sharing select will need more work though).
+ */
+#if !defined LWIP_SOCKET_OFFSET || defined __DOXYGEN__
+#define LWIP_SOCKET_OFFSET 0
+#endif
+
+/**
+ * LWIP_TCP_KEEPALIVE==1: Enable TCP_KEEPIDLE, TCP_KEEPINTVL and TCP_KEEPCNT
+ * options processing. Note that TCP_KEEPIDLE and TCP_KEEPINTVL have to be set
+ * in seconds. (does not require sockets.c, and will affect tcp.c)
+ */
+#if !defined LWIP_TCP_KEEPALIVE || defined __DOXYGEN__
+#define LWIP_TCP_KEEPALIVE 0
+#endif
+
+/**
+ * LWIP_SO_SNDTIMEO==1: Enable send timeout for sockets/netconns and
+ * SO_SNDTIMEO processing.
+ */
+#if !defined LWIP_SO_SNDTIMEO || defined __DOXYGEN__
+#define LWIP_SO_SNDTIMEO 0
+#endif
+
+/**
+ * LWIP_SO_RCVTIMEO==1: Enable receive timeout for sockets/netconns and
+ * SO_RCVTIMEO processing.
+ */
+#if !defined LWIP_SO_RCVTIMEO || defined __DOXYGEN__
+#define LWIP_SO_RCVTIMEO 0
+#endif
+
+/**
+ * LWIP_SO_SNDRCVTIMEO_NONSTANDARD==1: SO_RCVTIMEO/SO_SNDTIMEO take an int
+ * (milliseconds, much like winsock does) instead of a struct timeval (default).
+ */
+#if !defined LWIP_SO_SNDRCVTIMEO_NONSTANDARD || defined __DOXYGEN__
+#define LWIP_SO_SNDRCVTIMEO_NONSTANDARD 0
+#endif
+
+/**
+ * LWIP_SO_RCVBUF==1: Enable SO_RCVBUF processing.
+ */
+#if !defined LWIP_SO_RCVBUF || defined __DOXYGEN__
+#define LWIP_SO_RCVBUF 0
+#endif
+
+/**
+ * LWIP_SO_LINGER==1: Enable SO_LINGER processing.
+ */
+#if !defined LWIP_SO_LINGER || defined __DOXYGEN__
+#define LWIP_SO_LINGER 0
+#endif
+
+/**
+ * If LWIP_SO_RCVBUF is used, this is the default value for recv_bufsize.
+ */
+#if !defined RECV_BUFSIZE_DEFAULT || defined __DOXYGEN__
+#define RECV_BUFSIZE_DEFAULT INT_MAX
+#endif
+
+/**
+ * By default, TCP socket/netconn close waits 20 seconds max to send the FIN
+ */
+#if !defined LWIP_TCP_CLOSE_TIMEOUT_MS_DEFAULT || defined __DOXYGEN__
+#define LWIP_TCP_CLOSE_TIMEOUT_MS_DEFAULT 20000
+#endif
+
+/**
+ * SO_REUSE==1: Enable SO_REUSEADDR option.
+ */
+#if !defined SO_REUSE || defined __DOXYGEN__
+#define SO_REUSE 0
+#endif
+
+/**
+ * SO_REUSE_RXTOALL==1: Pass a copy of incoming broadcast/multicast packets
+ * to all local matches if SO_REUSEADDR is turned on.
+ * WARNING: Adds a memcpy for every packet if passing to more than one pcb!
+ */
+#if !defined SO_REUSE_RXTOALL || defined __DOXYGEN__
+#define SO_REUSE_RXTOALL 0
+#endif
+
+/**
+ * LWIP_FIONREAD_LINUXMODE==0 (default): ioctl/FIONREAD returns the amount of
+ * pending data in the network buffer. This is the way windows does it. It's
+ * the default for lwIP since it is smaller.
+ * LWIP_FIONREAD_LINUXMODE==1: ioctl/FIONREAD returns the size of the next
+ * pending datagram in bytes. This is the way linux does it. This code is only
+ * here for compatibility.
+ */
+#if !defined LWIP_FIONREAD_LINUXMODE || defined __DOXYGEN__
+#define LWIP_FIONREAD_LINUXMODE 0
+#endif
+/**
+ * @}
+ */
+
+/*
+ ----------------------------------------
+ ---------- Statistics options ----------
+ ----------------------------------------
+*/
+/**
+ * @defgroup lwip_opts_stats Statistics
+ * @ingroup lwip_opts_debug
+ * @{
+ */
+/**
+ * LWIP_STATS==1: Enable statistics collection in lwip_stats.
+ */
+#if !defined LWIP_STATS || defined __DOXYGEN__
+#define LWIP_STATS 1
+#endif
+
+#if LWIP_STATS
+
+/**
+ * LWIP_STATS_DISPLAY==1: Compile in the statistics output functions.
+ */
+#if !defined LWIP_STATS_DISPLAY || defined __DOXYGEN__
+#define LWIP_STATS_DISPLAY 0
+#endif
+
+/**
+ * LINK_STATS==1: Enable link stats.
+ */
+#if !defined LINK_STATS || defined __DOXYGEN__
+#define LINK_STATS 1
+#endif
+
+/**
+ * ETHARP_STATS==1: Enable etharp stats.
+ */
+#if !defined ETHARP_STATS || defined __DOXYGEN__
+#define ETHARP_STATS (LWIP_ARP)
+#endif
+
+/**
+ * IP_STATS==1: Enable IP stats.
+ */
+#if !defined IP_STATS || defined __DOXYGEN__
+#define IP_STATS 1
+#endif
+
+/**
+ * IPFRAG_STATS==1: Enable IP fragmentation stats. Default is
+ * on if using either frag or reass.
+ */
+#if !defined IPFRAG_STATS || defined __DOXYGEN__
+#define IPFRAG_STATS (IP_REASSEMBLY || IP_FRAG)
+#endif
+
+/**
+ * ICMP_STATS==1: Enable ICMP stats.
+ */
+#if !defined ICMP_STATS || defined __DOXYGEN__
+#define ICMP_STATS 1
+#endif
+
+/**
+ * IGMP_STATS==1: Enable IGMP stats.
+ */
+#if !defined IGMP_STATS || defined __DOXYGEN__
+#define IGMP_STATS (LWIP_IGMP)
+#endif
+
+/**
+ * UDP_STATS==1: Enable UDP stats. Default is on if
+ * UDP enabled, otherwise off.
+ */
+#if !defined UDP_STATS || defined __DOXYGEN__
+#define UDP_STATS (LWIP_UDP)
+#endif
+
+/**
+ * TCP_STATS==1: Enable TCP stats. Default is on if TCP
+ * enabled, otherwise off.
+ */
+#if !defined TCP_STATS || defined __DOXYGEN__
+#define TCP_STATS (LWIP_TCP)
+#endif
+
+/**
+ * MEM_STATS==1: Enable mem.c stats.
+ */
+#if !defined MEM_STATS || defined __DOXYGEN__
+#define MEM_STATS ((MEM_LIBC_MALLOC == 0) && (MEM_USE_POOLS == 0))
+#endif
+
+/**
+ * MEMP_STATS==1: Enable memp.c pool stats.
+ */
+#if !defined MEMP_STATS || defined __DOXYGEN__
+#define MEMP_STATS (MEMP_MEM_MALLOC == 0)
+#endif
+
+/**
+ * SYS_STATS==1: Enable system stats (sem and mbox counts, etc).
+ */
+#if !defined SYS_STATS || defined __DOXYGEN__
+#define SYS_STATS (NO_SYS == 0)
+#endif
+
+/**
+ * IP6_STATS==1: Enable IPv6 stats.
+ */
+#if !defined IP6_STATS || defined __DOXYGEN__
+#define IP6_STATS (LWIP_IPV6)
+#endif
+
+/**
+ * ICMP6_STATS==1: Enable ICMP for IPv6 stats.
+ */
+#if !defined ICMP6_STATS || defined __DOXYGEN__
+#define ICMP6_STATS (LWIP_IPV6 && LWIP_ICMP6)
+#endif
+
+/**
+ * IP6_FRAG_STATS==1: Enable IPv6 fragmentation stats.
+ */
+#if !defined IP6_FRAG_STATS || defined __DOXYGEN__
+#define IP6_FRAG_STATS (LWIP_IPV6 && (LWIP_IPV6_FRAG || LWIP_IPV6_REASS))
+#endif
+
+/**
+ * MLD6_STATS==1: Enable MLD for IPv6 stats.
+ */
+#if !defined MLD6_STATS || defined __DOXYGEN__
+#define MLD6_STATS (LWIP_IPV6 && LWIP_IPV6_MLD)
+#endif
+
+/**
+ * ND6_STATS==1: Enable Neighbor discovery for IPv6 stats.
+ */
+#if !defined ND6_STATS || defined __DOXYGEN__
+#define ND6_STATS (LWIP_IPV6)
+#endif
+
+/**
+ * MIB2_STATS==1: Stats for SNMP MIB2.
+ */
+#if !defined MIB2_STATS || defined __DOXYGEN__
+#define MIB2_STATS 0
+#endif
+
+#else
+
+#define LINK_STATS 0
+#define ETHARP_STATS 0
+#define IP_STATS 0
+#define IPFRAG_STATS 0
+#define ICMP_STATS 0
+#define IGMP_STATS 0
+#define UDP_STATS 0
+#define TCP_STATS 0
+#define MEM_STATS 0
+#define MEMP_STATS 0
+#define SYS_STATS 0
+#define LWIP_STATS_DISPLAY 0
+#define IP6_STATS 0
+#define ICMP6_STATS 0
+#define IP6_FRAG_STATS 0
+#define MLD6_STATS 0
+#define ND6_STATS 0
+#define MIB2_STATS 0
+
+#endif /* LWIP_STATS */
+/**
+ * @}
+ */
+
+/*
+ --------------------------------------
+ ---------- Checksum options ----------
+ --------------------------------------
+*/
+/**
+ * @defgroup lwip_opts_checksum Checksum
+ * @ingroup lwip_opts_infrastructure
+ * @{
+ */
+/**
+ * LWIP_CHECKSUM_CTRL_PER_NETIF==1: Checksum generation/check can be enabled/disabled
+ * per netif.
+ * ATTENTION: if enabled, the CHECKSUM_GEN_* and CHECKSUM_CHECK_* defines must be enabled!
+ */
+#if !defined LWIP_CHECKSUM_CTRL_PER_NETIF || defined __DOXYGEN__
+#define LWIP_CHECKSUM_CTRL_PER_NETIF 0
+#endif
+
+/**
+ * CHECKSUM_GEN_IP==1: Generate checksums in software for outgoing IP packets.
+ */
+#if !defined CHECKSUM_GEN_IP || defined __DOXYGEN__
+#define CHECKSUM_GEN_IP 1
+#endif
+
+/**
+ * CHECKSUM_GEN_UDP==1: Generate checksums in software for outgoing UDP packets.
+ */
+#if !defined CHECKSUM_GEN_UDP || defined __DOXYGEN__
+#define CHECKSUM_GEN_UDP 1
+#endif
+
+/**
+ * CHECKSUM_GEN_TCP==1: Generate checksums in software for outgoing TCP packets.
+ */
+#if !defined CHECKSUM_GEN_TCP || defined __DOXYGEN__
+#define CHECKSUM_GEN_TCP 1
+#endif
+
+/**
+ * CHECKSUM_GEN_ICMP==1: Generate checksums in software for outgoing ICMP packets.
+ */
+#if !defined CHECKSUM_GEN_ICMP || defined __DOXYGEN__
+#define CHECKSUM_GEN_ICMP 1
+#endif
+
+/**
+ * CHECKSUM_GEN_ICMP6==1: Generate checksums in software for outgoing ICMP6 packets.
+ */
+#if !defined CHECKSUM_GEN_ICMP6 || defined __DOXYGEN__
+#define CHECKSUM_GEN_ICMP6 1
+#endif
+
+/**
+ * CHECKSUM_CHECK_IP==1: Check checksums in software for incoming IP packets.
+ */
+#if !defined CHECKSUM_CHECK_IP || defined __DOXYGEN__
+#define CHECKSUM_CHECK_IP 1
+#endif
+
+/**
+ * CHECKSUM_CHECK_UDP==1: Check checksums in software for incoming UDP packets.
+ */
+#if !defined CHECKSUM_CHECK_UDP || defined __DOXYGEN__
+#define CHECKSUM_CHECK_UDP 1
+#endif
+
+/**
+ * CHECKSUM_CHECK_TCP==1: Check checksums in software for incoming TCP packets.
+ */
+#if !defined CHECKSUM_CHECK_TCP || defined __DOXYGEN__
+#define CHECKSUM_CHECK_TCP 1
+#endif
+
+/**
+ * CHECKSUM_CHECK_ICMP==1: Check checksums in software for incoming ICMP packets.
+ */
+#if !defined CHECKSUM_CHECK_ICMP || defined __DOXYGEN__
+#define CHECKSUM_CHECK_ICMP 1
+#endif
+
+/**
+ * CHECKSUM_CHECK_ICMP6==1: Check checksums in software for incoming ICMPv6 packets
+ */
+#if !defined CHECKSUM_CHECK_ICMP6 || defined __DOXYGEN__
+#define CHECKSUM_CHECK_ICMP6 1
+#endif
+
+/**
+ * LWIP_CHECKSUM_ON_COPY==1: Calculate checksum when copying data from
+ * application buffers to pbufs.
+ */
+#if !defined LWIP_CHECKSUM_ON_COPY || defined __DOXYGEN__
+#define LWIP_CHECKSUM_ON_COPY 0
+#endif
+/**
+ * @}
+ */
+
+/*
+ ---------------------------------------
+ ---------- IPv6 options ---------------
+ ---------------------------------------
+*/
+/**
+ * @defgroup lwip_opts_ipv6 IPv6
+ * @ingroup lwip_opts
+ * @{
+ */
+/**
+ * LWIP_IPV6==1: Enable IPv6
+ */
+#if !defined LWIP_IPV6 || defined __DOXYGEN__
+#define LWIP_IPV6 0
+#endif
+
+/**
+ * LWIP_IPV6_NUM_ADDRESSES: Number of IPv6 addresses per netif.
+ */
+#if !defined LWIP_IPV6_NUM_ADDRESSES || defined __DOXYGEN__
+#define LWIP_IPV6_NUM_ADDRESSES 3
+#endif
+
+/**
+ * LWIP_IPV6_FORWARD==1: Forward IPv6 packets across netifs
+ */
+#if !defined LWIP_IPV6_FORWARD || defined __DOXYGEN__
+#define LWIP_IPV6_FORWARD 0
+#endif
+
+/**
+ * LWIP_IPV6_FRAG==1: Fragment outgoing IPv6 packets that are too big.
+ */
+#if !defined LWIP_IPV6_FRAG || defined __DOXYGEN__
+#define LWIP_IPV6_FRAG 0
+#endif
+
+/**
+ * LWIP_IPV6_REASS==1: reassemble incoming IPv6 packets that fragmented
+ */
+#if !defined LWIP_IPV6_REASS || defined __DOXYGEN__
+#define LWIP_IPV6_REASS (LWIP_IPV6)
+#endif
+
+/**
+ * LWIP_IPV6_SEND_ROUTER_SOLICIT==1: Send router solicitation messages during
+ * network startup.
+ */
+#if !defined LWIP_IPV6_SEND_ROUTER_SOLICIT || defined __DOXYGEN__
+#define LWIP_IPV6_SEND_ROUTER_SOLICIT 1
+#endif
+
+/**
+ * LWIP_IPV6_AUTOCONFIG==1: Enable stateless address autoconfiguration as per RFC 4862.
+ */
+#if !defined LWIP_IPV6_AUTOCONFIG || defined __DOXYGEN__
+#define LWIP_IPV6_AUTOCONFIG (LWIP_IPV6)
+#endif
+
+/**
+ * LWIP_IPV6_DUP_DETECT_ATTEMPTS=[0..7]: Number of duplicate address detection attempts.
+ */
+#if !defined LWIP_IPV6_DUP_DETECT_ATTEMPTS || defined __DOXYGEN__
+#define LWIP_IPV6_DUP_DETECT_ATTEMPTS 1
+#endif
+/**
+ * @}
+ */
+
+/**
+ * @defgroup lwip_opts_icmp6 ICMP6
+ * @ingroup lwip_opts_ipv6
+ * @{
+ */
+/**
+ * LWIP_ICMP6==1: Enable ICMPv6 (mandatory per RFC)
+ */
+#if !defined LWIP_ICMP6 || defined __DOXYGEN__
+#define LWIP_ICMP6 (LWIP_IPV6)
+#endif
+
+/**
+ * LWIP_ICMP6_DATASIZE: bytes from original packet to send back in
+ * ICMPv6 error messages.
+ */
+#if !defined LWIP_ICMP6_DATASIZE || defined __DOXYGEN__
+#define LWIP_ICMP6_DATASIZE 8
+#endif
+
+/**
+ * LWIP_ICMP6_HL: default hop limit for ICMPv6 messages
+ */
+#if !defined LWIP_ICMP6_HL || defined __DOXYGEN__
+#define LWIP_ICMP6_HL 255
+#endif
+/**
+ * @}
+ */
+
+/**
+ * @defgroup lwip_opts_mld6 Multicast listener discovery
+ * @ingroup lwip_opts_ipv6
+ * @{
+ */
+/**
+ * LWIP_IPV6_MLD==1: Enable multicast listener discovery protocol.
+ * If LWIP_IPV6 is enabled but this setting is disabled, the MAC layer must
+ * indiscriminately pass all inbound IPv6 multicast traffic to lwIP.
+ */
+#if !defined LWIP_IPV6_MLD || defined __DOXYGEN__
+#define LWIP_IPV6_MLD (LWIP_IPV6)
+#endif
+
+/**
+ * MEMP_NUM_MLD6_GROUP: Max number of IPv6 multicast groups that can be joined.
+ * There must be enough groups so that each netif can join the solicited-node
+ * multicast group for each of its local addresses, plus one for MDNS if
+ * applicable, plus any number of groups to be joined on UDP sockets.
+ */
+#if !defined MEMP_NUM_MLD6_GROUP || defined __DOXYGEN__
+#define MEMP_NUM_MLD6_GROUP 4
+#endif
+/**
+ * @}
+ */
+
+/**
+ * @defgroup lwip_opts_nd6 Neighbor discovery
+ * @ingroup lwip_opts_ipv6
+ * @{
+ */
+/**
+ * LWIP_ND6_QUEUEING==1: queue outgoing IPv6 packets while MAC address
+ * is being resolved.
+ */
+#if !defined LWIP_ND6_QUEUEING || defined __DOXYGEN__
+#define LWIP_ND6_QUEUEING (LWIP_IPV6)
+#endif
+
+/**
+ * MEMP_NUM_ND6_QUEUE: Max number of IPv6 packets to queue during MAC resolution.
+ */
+#if !defined MEMP_NUM_ND6_QUEUE || defined __DOXYGEN__
+#define MEMP_NUM_ND6_QUEUE 20
+#endif
+
+/**
+ * LWIP_ND6_NUM_NEIGHBORS: Number of entries in IPv6 neighbor cache
+ */
+#if !defined LWIP_ND6_NUM_NEIGHBORS || defined __DOXYGEN__
+#define LWIP_ND6_NUM_NEIGHBORS 10
+#endif
+
+/**
+ * LWIP_ND6_NUM_DESTINATIONS: number of entries in IPv6 destination cache
+ */
+#if !defined LWIP_ND6_NUM_DESTINATIONS || defined __DOXYGEN__
+#define LWIP_ND6_NUM_DESTINATIONS 10
+#endif
+
+/**
+ * LWIP_ND6_NUM_PREFIXES: number of entries in IPv6 on-link prefixes cache
+ */
+#if !defined LWIP_ND6_NUM_PREFIXES || defined __DOXYGEN__
+#define LWIP_ND6_NUM_PREFIXES 5
+#endif
+
+/**
+ * LWIP_ND6_NUM_ROUTERS: number of entries in IPv6 default router cache
+ */
+#if !defined LWIP_ND6_NUM_ROUTERS || defined __DOXYGEN__
+#define LWIP_ND6_NUM_ROUTERS 3
+#endif
+
+/**
+ * LWIP_ND6_MAX_MULTICAST_SOLICIT: max number of multicast solicit messages to send
+ * (neighbor solicit and router solicit)
+ */
+#if !defined LWIP_ND6_MAX_MULTICAST_SOLICIT || defined __DOXYGEN__
+#define LWIP_ND6_MAX_MULTICAST_SOLICIT 3
+#endif
+
+/**
+ * LWIP_ND6_MAX_UNICAST_SOLICIT: max number of unicast neighbor solicitation messages
+ * to send during neighbor reachability detection.
+ */
+#if !defined LWIP_ND6_MAX_UNICAST_SOLICIT || defined __DOXYGEN__
+#define LWIP_ND6_MAX_UNICAST_SOLICIT 3
+#endif
+
+/**
+ * Unused: See ND RFC (time in milliseconds).
+ */
+#if !defined LWIP_ND6_MAX_ANYCAST_DELAY_TIME || defined __DOXYGEN__
+#define LWIP_ND6_MAX_ANYCAST_DELAY_TIME 1000
+#endif
+
+/**
+ * Unused: See ND RFC
+ */
+#if !defined LWIP_ND6_MAX_NEIGHBOR_ADVERTISEMENT || defined __DOXYGEN__
+#define LWIP_ND6_MAX_NEIGHBOR_ADVERTISEMENT 3
+#endif
+
+/**
+ * LWIP_ND6_REACHABLE_TIME: default neighbor reachable time (in milliseconds).
+ * May be updated by router advertisement messages.
+ */
+#if !defined LWIP_ND6_REACHABLE_TIME || defined __DOXYGEN__
+#define LWIP_ND6_REACHABLE_TIME 30000
+#endif
+
+/**
+ * LWIP_ND6_RETRANS_TIMER: default retransmission timer for solicitation messages
+ */
+#if !defined LWIP_ND6_RETRANS_TIMER || defined __DOXYGEN__
+#define LWIP_ND6_RETRANS_TIMER 1000
+#endif
+
+/**
+ * LWIP_ND6_DELAY_FIRST_PROBE_TIME: Delay before first unicast neighbor solicitation
+ * message is sent, during neighbor reachability detection.
+ */
+#if !defined LWIP_ND6_DELAY_FIRST_PROBE_TIME || defined __DOXYGEN__
+#define LWIP_ND6_DELAY_FIRST_PROBE_TIME 5000
+#endif
+
+/**
+ * LWIP_ND6_ALLOW_RA_UPDATES==1: Allow Router Advertisement messages to update
+ * Reachable time and retransmission timers, and netif MTU.
+ */
+#if !defined LWIP_ND6_ALLOW_RA_UPDATES || defined __DOXYGEN__
+#define LWIP_ND6_ALLOW_RA_UPDATES 1
+#endif
+
+/**
+ * LWIP_ND6_TCP_REACHABILITY_HINTS==1: Allow TCP to provide Neighbor Discovery
+ * with reachability hints for connected destinations. This helps avoid sending
+ * unicast neighbor solicitation messages.
+ */
+#if !defined LWIP_ND6_TCP_REACHABILITY_HINTS || defined __DOXYGEN__
+#define LWIP_ND6_TCP_REACHABILITY_HINTS 1
+#endif
+
+/**
+ * LWIP_ND6_RDNSS_MAX_DNS_SERVERS > 0: Use IPv6 Router Advertisement Recursive
+ * DNS Server Option (as per RFC 6106) to copy a defined maximum number of DNS
+ * servers to the DNS module.
+ */
+#if !defined LWIP_ND6_RDNSS_MAX_DNS_SERVERS || defined __DOXYGEN__
+#define LWIP_ND6_RDNSS_MAX_DNS_SERVERS 0
+#endif
+/**
+ * @}
+ */
+
+/**
+ * LWIP_IPV6_DHCP6==1: enable DHCPv6 stateful address autoconfiguration.
+ */
+#if !defined LWIP_IPV6_DHCP6 || defined __DOXYGEN__
+#define LWIP_IPV6_DHCP6 0
+#endif
+
+/*
+ ---------------------------------------
+ ---------- Hook options ---------------
+ ---------------------------------------
+*/
+
+/**
+ * @defgroup lwip_opts_hooks Hooks
+ * @ingroup lwip_opts_infrastructure
+ * Hooks are undefined by default, define them to a function if you need them.
+ * @{
+ */
+
+/**
+ * LWIP_HOOK_FILENAME: Custom filename to #include in files that provide hooks.
+ * Declare your hook function prototypes in there, you may also #include all headers
+ * providing data types that are need in this file.
+ */
+#ifdef __DOXYGEN__
+#define LWIP_HOOK_FILENAME "path/to/my/lwip_hooks.h"
+#endif
+
+/**
+ * LWIP_HOOK_TCP_ISN:
+ * Hook for generation of the Initial Sequence Number (ISN) for a new TCP
+ * connection. The default lwIP ISN generation algorithm is very basic and may
+ * allow for TCP spoofing attacks. This hook provides the means to implement
+ * the standardized ISN generation algorithm from RFC 6528 (see contrib/adons/tcp_isn),
+ * or any other desired algorithm as a replacement.
+ * Called from tcp_connect() and tcp_listen_input() when an ISN is needed for
+ * a new TCP connection, if TCP support (@ref LWIP_TCP) is enabled.\n
+ * Signature: u32_t my_hook_tcp_isn(const ip_addr_t* local_ip, u16_t local_port, const ip_addr_t* remote_ip, u16_t remote_port);
+ * - it may be necessary to use "struct ip_addr" (ip4_addr, ip6_addr) instead of "ip_addr_t" in function declarations\n
+ * Arguments:
+ * - local_ip: pointer to the local IP address of the connection
+ * - local_port: local port number of the connection (host-byte order)
+ * - remote_ip: pointer to the remote IP address of the connection
+ * - remote_port: remote port number of the connection (host-byte order)\n
+ * Return value:
+ * - the 32-bit Initial Sequence Number to use for the new TCP connection.
+ */
+#ifdef __DOXYGEN__
+#define LWIP_HOOK_TCP_ISN(local_ip, local_port, remote_ip, remote_port)
+#endif
+
+/**
+ * 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).
+ */
+#ifdef __DOXYGEN__
+#define LWIP_HOOK_IP4_INPUT(pbuf, input_netif)
+#endif
+
+/**
+ * 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.
+ */
+#ifdef __DOXYGEN__
+#define LWIP_HOOK_IP4_ROUTE()
+#endif
+
+/**
+ * LWIP_HOOK_IP4_ROUTE_SRC(dest, src):
+ * - source-based routing for IPv4 (see LWIP_HOOK_IP4_ROUTE(), src may be NULL)
+ */
+#ifdef __DOXYGEN__
+#define LWIP_HOOK_IP4_ROUTE_SRC(dest, src)
+#endif
+
+/**
+ * LWIP_HOOK_ETHARP_GET_GW(netif, dest):
+ * - called from etharp_output() (IPv4)
+ * - netif: the netif used for sending
+ * - dest: the destination IPv4 address
+ * Returns the IPv4 address of the gateway to handle the specified destination
+ * IPv4 address. If NULL is returned, the netif's default gateway is used.
+ * The returned address MUST be directly reachable on the specified netif!
+ * This function is meant to implement advanced IPv4 routing together with
+ * LWIP_HOOK_IP4_ROUTE(). The actual routing/gateway table implementation is
+ * not part of lwIP but can e.g. be hidden in the netif's state argument.
+*/
+#ifdef __DOXYGEN__
+#define LWIP_HOOK_ETHARP_GET_GW(netif, dest)
+#endif
+
+/**
+ * LWIP_HOOK_IP6_INPUT(pbuf, input_netif):
+ * - called from ip6_input() (IPv6)
+ * - pbuf: received struct pbuf passed to ip6_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).
+ */
+#ifdef __DOXYGEN__
+#define LWIP_HOOK_IP6_INPUT(pbuf, input_netif)
+#endif
+
+/**
+ * LWIP_HOOK_IP6_ROUTE(src, dest):
+ * - called from ip6_route() (IPv6)
+ * - src: sourc IPv6 address
+ * - dest: destination IPv6 address
+ * Returns the destination netif or NULL if no destination netif is found. In
+ * that case, ip6_route() continues as normal.
+ */
+#ifdef __DOXYGEN__
+#define LWIP_HOOK_IP6_ROUTE(src, dest)
+#endif
+
+/**
+ * LWIP_HOOK_ND6_GET_GW(netif, dest):
+ * - called from nd6_get_next_hop_entry() (IPv6)
+ * - netif: the netif used for sending
+ * - dest: the destination IPv6 address
+ * Returns the IPv6 address of the next hop to handle the specified destination
+ * IPv6 address. If NULL is returned, a NDP-discovered router is used instead.
+ * The returned address MUST be directly reachable on the specified netif!
+ * This function is meant to implement advanced IPv6 routing together with
+ * LWIP_HOOK_IP6_ROUTE(). The actual routing/gateway table implementation is
+ * not part of lwIP but can e.g. be hidden in the netif's state argument.
+*/
+#ifdef __DOXYGEN__
+#define LWIP_HOOK_ND6_GET_GW(netif, dest)
+#endif
+
+/**
+ * LWIP_HOOK_VLAN_CHECK(netif, eth_hdr, vlan_hdr):
+ * - called from ethernet_input() if VLAN support is enabled
+ * - netif: struct netif on which the packet has been received
+ * - eth_hdr: struct eth_hdr of the packet
+ * - vlan_hdr: struct eth_vlan_hdr of the packet
+ * Return values:
+ * - 0: Packet must be dropped.
+ * - != 0: Packet must be accepted.
+ */
+#ifdef __DOXYGEN__
+#define LWIP_HOOK_VLAN_CHECK(netif, eth_hdr, vlan_hdr)
+#endif
+
+/**
+ * LWIP_HOOK_VLAN_SET:
+ * Hook can be used to set prio_vid field of vlan_hdr. If you need to store data
+ * on per-netif basis to implement this callback, see @ref netif_cd.
+ * Called from ethernet_output() if VLAN support (@ref ETHARP_SUPPORT_VLAN) is enabled.\n
+ * Signature: s32_t my_hook_vlan_set(struct netif* netif, struct pbuf* pbuf, const struct eth_addr* src, const struct eth_addr* dst, u16_t eth_type);\n
+ * Arguments:
+ * - netif: struct netif that the packet will be sent through
+ * - p: struct pbuf packet to be sent
+ * - src: source eth address
+ * - dst: destination eth address
+ * - eth_type: ethernet type to packet to be sent\n
+ *
+ *
+ * Return values:
+ * - &lt;0: Packet shall not contain VLAN header.
+ * - 0 &lt;= return value &lt;= 0xFFFF: Packet shall contain VLAN header. Return value is prio_vid in host byte order.
+ */
+#ifdef __DOXYGEN__
+#define LWIP_HOOK_VLAN_SET(netif, p, src, dst, eth_type)
+#endif
+
+/**
+ * LWIP_HOOK_MEMP_AVAILABLE(memp_t_type):
+ * - called from memp_free() when a memp pool was empty and an item is now available
+ */
+#ifdef __DOXYGEN__
+#define LWIP_HOOK_MEMP_AVAILABLE(memp_t_type)
+#endif
+
+/**
+ * LWIP_HOOK_UNKNOWN_ETH_PROTOCOL(pbuf, netif):
+ * Called from ethernet_input() when an unknown eth type is encountered.
+ * Return ERR_OK if packet is accepted, any error code otherwise.
+ * Payload points to ethernet header!
+ */
+#ifdef __DOXYGEN__
+#define LWIP_HOOK_UNKNOWN_ETH_PROTOCOL(pbuf, netif)
+#endif
+/**
+ * @}
+ */
+
+/*
+ ---------------------------------------
+ ---------- Debugging options ----------
+ ---------------------------------------
+*/
+/**
+ * @defgroup lwip_opts_debugmsg Debug messages
+ * @ingroup lwip_opts_debug
+ * @{
+ */
+/**
+ * LWIP_DBG_MIN_LEVEL: After masking, the value of the debug is
+ * compared against this value. If it is smaller, then debugging
+ * messages are written.
+ * @see debugging_levels
+ */
+#if !defined LWIP_DBG_MIN_LEVEL || defined __DOXYGEN__
+#define LWIP_DBG_MIN_LEVEL LWIP_DBG_LEVEL_ALL
+#endif
+
+/**
+ * LWIP_DBG_TYPES_ON: A mask that can be used to globally enable/disable
+ * debug messages of certain types.
+ * @see debugging_levels
+ */
+#if !defined LWIP_DBG_TYPES_ON || defined __DOXYGEN__
+#define LWIP_DBG_TYPES_ON LWIP_DBG_ON
+#endif
+
+/**
+ * ETHARP_DEBUG: Enable debugging in etharp.c.
+ */
+#if !defined ETHARP_DEBUG || defined __DOXYGEN__
+#define ETHARP_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * NETIF_DEBUG: Enable debugging in netif.c.
+ */
+#if !defined NETIF_DEBUG || defined __DOXYGEN__
+#define NETIF_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * PBUF_DEBUG: Enable debugging in pbuf.c.
+ */
+#if !defined PBUF_DEBUG || defined __DOXYGEN__
+#define PBUF_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * API_LIB_DEBUG: Enable debugging in api_lib.c.
+ */
+#if !defined API_LIB_DEBUG || defined __DOXYGEN__
+#define API_LIB_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * API_MSG_DEBUG: Enable debugging in api_msg.c.
+ */
+#if !defined API_MSG_DEBUG || defined __DOXYGEN__
+#define API_MSG_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * SOCKETS_DEBUG: Enable debugging in sockets.c.
+ */
+#if !defined SOCKETS_DEBUG || defined __DOXYGEN__
+#define SOCKETS_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * ICMP_DEBUG: Enable debugging in icmp.c.
+ */
+#if !defined ICMP_DEBUG || defined __DOXYGEN__
+#define ICMP_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * IGMP_DEBUG: Enable debugging in igmp.c.
+ */
+#if !defined IGMP_DEBUG || defined __DOXYGEN__
+#define IGMP_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * INET_DEBUG: Enable debugging in inet.c.
+ */
+#if !defined INET_DEBUG || defined __DOXYGEN__
+#define INET_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * IP_DEBUG: Enable debugging for IP.
+ */
+#if !defined IP_DEBUG || defined __DOXYGEN__
+#define IP_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * IP_REASS_DEBUG: Enable debugging in ip_frag.c for both frag & reass.
+ */
+#if !defined IP_REASS_DEBUG || defined __DOXYGEN__
+#define IP_REASS_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * RAW_DEBUG: Enable debugging in raw.c.
+ */
+#if !defined RAW_DEBUG || defined __DOXYGEN__
+#define RAW_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * MEM_DEBUG: Enable debugging in mem.c.
+ */
+#if !defined MEM_DEBUG || defined __DOXYGEN__
+#define MEM_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * MEMP_DEBUG: Enable debugging in memp.c.
+ */
+#if !defined MEMP_DEBUG || defined __DOXYGEN__
+#define MEMP_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * SYS_DEBUG: Enable debugging in sys.c.
+ */
+#if !defined SYS_DEBUG || defined __DOXYGEN__
+#define SYS_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * TIMERS_DEBUG: Enable debugging in timers.c.
+ */
+#if !defined TIMERS_DEBUG || defined __DOXYGEN__
+#define TIMERS_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * TCP_DEBUG: Enable debugging for TCP.
+ */
+#if !defined TCP_DEBUG || defined __DOXYGEN__
+#define TCP_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * TCP_INPUT_DEBUG: Enable debugging in tcp_in.c for incoming debug.
+ */
+#if !defined TCP_INPUT_DEBUG || defined __DOXYGEN__
+#define TCP_INPUT_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * TCP_FR_DEBUG: Enable debugging in tcp_in.c for fast retransmit.
+ */
+#if !defined TCP_FR_DEBUG || defined __DOXYGEN__
+#define TCP_FR_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * TCP_RTO_DEBUG: Enable debugging in TCP for retransmit
+ * timeout.
+ */
+#if !defined TCP_RTO_DEBUG || defined __DOXYGEN__
+#define TCP_RTO_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * TCP_CWND_DEBUG: Enable debugging for TCP congestion window.
+ */
+#if !defined TCP_CWND_DEBUG || defined __DOXYGEN__
+#define TCP_CWND_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * TCP_WND_DEBUG: Enable debugging in tcp_in.c for window updating.
+ */
+#if !defined TCP_WND_DEBUG || defined __DOXYGEN__
+#define TCP_WND_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * TCP_OUTPUT_DEBUG: Enable debugging in tcp_out.c output functions.
+ */
+#if !defined TCP_OUTPUT_DEBUG || defined __DOXYGEN__
+#define TCP_OUTPUT_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * TCP_RST_DEBUG: Enable debugging for TCP with the RST message.
+ */
+#if !defined TCP_RST_DEBUG || defined __DOXYGEN__
+#define TCP_RST_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * TCP_QLEN_DEBUG: Enable debugging for TCP queue lengths.
+ */
+#if !defined TCP_QLEN_DEBUG || defined __DOXYGEN__
+#define TCP_QLEN_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * UDP_DEBUG: Enable debugging in UDP.
+ */
+#if !defined UDP_DEBUG || defined __DOXYGEN__
+#define UDP_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * TCPIP_DEBUG: Enable debugging in tcpip.c.
+ */
+#if !defined TCPIP_DEBUG || defined __DOXYGEN__
+#define TCPIP_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * SLIP_DEBUG: Enable debugging in slipif.c.
+ */
+#if !defined SLIP_DEBUG || defined __DOXYGEN__
+#define SLIP_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * DHCP_DEBUG: Enable debugging in dhcp.c.
+ */
+#if !defined DHCP_DEBUG || defined __DOXYGEN__
+#define DHCP_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * AUTOIP_DEBUG: Enable debugging in autoip.c.
+ */
+#if !defined AUTOIP_DEBUG || defined __DOXYGEN__
+#define AUTOIP_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * DNS_DEBUG: Enable debugging for DNS.
+ */
+#if !defined DNS_DEBUG || defined __DOXYGEN__
+#define DNS_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * IP6_DEBUG: Enable debugging for IPv6.
+ */
+#if !defined IP6_DEBUG || defined __DOXYGEN__
+#define IP6_DEBUG LWIP_DBG_OFF
+#endif
+/**
+ * @}
+ */
+
+/*
+ --------------------------------------------------
+ ---------- Performance tracking options ----------
+ --------------------------------------------------
+*/
+/**
+ * @defgroup lwip_opts_perf Performance
+ * @ingroup lwip_opts_debug
+ * @{
+ */
+/**
+ * LWIP_PERF: Enable performance testing for lwIP
+ * (if enabled, arch/perf.h is included)
+ */
+#if !defined LWIP_PERF || defined __DOXYGEN__
+#define LWIP_PERF 0
+#endif
+/**
+ * @}
+ */
+
+#endif /* LWIP_HDR_OPT_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/pbuf.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/pbuf.h
new file mode 100644
index 0000000..9061046
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/pbuf.h
@@ -0,0 +1,263 @@
+/**
+ * @file
+ * pbuf API
+ */
+
+/*
+ * 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_HDR_PBUF_H
+#define LWIP_HDR_PBUF_H
+
+#include "lwip/opt.h"
+#include "lwip/err.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** LWIP_SUPPORT_CUSTOM_PBUF==1: Custom pbufs behave much like their pbuf type
+ * but they are allocated by external code (initialised by calling
+ * pbuf_alloced_custom()) and when pbuf_free gives up their last reference, they
+ * are freed by calling pbuf_custom->custom_free_function().
+ * Currently, the pbuf_custom code is only needed for one specific configuration
+ * of IP_FRAG, unless required by external driver/application code. */
+#ifndef LWIP_SUPPORT_CUSTOM_PBUF
+#define LWIP_SUPPORT_CUSTOM_PBUF ((IP_FRAG && !LWIP_NETIF_TX_SINGLE_PBUF) || (LWIP_IPV6 && LWIP_IPV6_FRAG))
+#endif
+
+/* @todo: We need a mechanism to prevent wasting memory in every pbuf
+ (TCP vs. UDP, IPv4 vs. IPv6: UDP/IPv4 packets may waste up to 28 bytes) */
+
+#define PBUF_TRANSPORT_HLEN 20
+#if LWIP_IPV6
+#define PBUF_IP_HLEN 40
+#else
+#define PBUF_IP_HLEN 20
+#endif
+
+/**
+ * @ingroup pbuf
+ * Enumeration of pbuf layers
+ */
+typedef enum {
+ /** Includes spare room for transport layer header, e.g. UDP header.
+ * Use this if you intend to pass the pbuf to functions like udp_send().
+ */
+ PBUF_TRANSPORT,
+ /** Includes spare room for IP header.
+ * Use this if you intend to pass the pbuf to functions like raw_send().
+ */
+ PBUF_IP,
+ /** Includes spare room for link layer header (ethernet header).
+ * Use this if you intend to pass the pbuf to functions like ethernet_output().
+ * @see PBUF_LINK_HLEN
+ */
+ PBUF_LINK,
+ /** Includes spare room for additional encapsulation header before ethernet
+ * headers (e.g. 802.11).
+ * Use this if you intend to pass the pbuf to functions like netif->linkoutput().
+ * @see PBUF_LINK_ENCAPSULATION_HLEN
+ */
+ PBUF_RAW_TX,
+ /** Use this for input packets in a netif driver when calling netif->input()
+ * in the most common case - ethernet-layer netif driver. */
+ PBUF_RAW
+} pbuf_layer;
+
+/**
+ * @ingroup pbuf
+ * Enumeration of pbuf types
+ */
+typedef enum {
+ /** pbuf data is stored in RAM, used for TX mostly, struct pbuf and its payload
+ are allocated in one piece of contiguous memory (so the first payload byte
+ can be calculated from struct pbuf).
+ pbuf_alloc() allocates PBUF_RAM pbufs as unchained pbufs (although that might
+ change in future versions).
+ This should be used for all OUTGOING packets (TX).*/
+ PBUF_RAM,
+ /** pbuf data is stored in ROM, i.e. struct pbuf and its payload are located in
+ totally different memory areas. Since it points to ROM, payload does not
+ have to be copied when queued for transmission. */
+ PBUF_ROM,
+ /** pbuf comes from the pbuf pool. Much like PBUF_ROM but payload might change
+ so it has to be duplicated when queued before transmitting, depending on
+ who has a 'ref' to it. */
+ PBUF_REF,
+ /** pbuf payload refers to RAM. This one comes from a pool and should be used
+ for RX. Payload can be chained (scatter-gather RX) but like PBUF_RAM, struct
+ pbuf and its payload are allocated in one piece of contiguous memory (so
+ the first payload byte can be calculated from struct pbuf).
+ Don't use this for TX, if the pool becomes empty e.g. because of TCP queuing,
+ you are unable to receive TCP acks! */
+ PBUF_POOL
+} pbuf_type;
+
+
+/** indicates this packet's data should be immediately passed to the application */
+#define PBUF_FLAG_PUSH 0x01U
+/** indicates this is a custom pbuf: pbuf_free calls pbuf_custom->custom_free_function()
+ when the last reference is released (plus custom PBUF_RAM cannot be trimmed) */
+#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
+
+/** Main packet buffer struct */
+struct pbuf {
+ /** next pbuf in singly linked pbuf chain */
+ struct pbuf *next;
+
+ /** pointer to the actual data in the buffer */
+ void *payload;
+
+ /**
+ * total length of this buffer and all next buffers in chain
+ * belonging to the same packet.
+ *
+ * For non-queue packet chains this is the invariant:
+ * p->tot_len == p->len + (p->next? p->next->tot_len: 0)
+ */
+ u16_t tot_len;
+
+ /** length of this buffer */
+ u16_t len;
+
+ /** pbuf_type as u8_t instead of enum to save space */
+ u8_t /*pbuf_type*/ type;
+
+ /** misc flags */
+ u8_t flags;
+
+ /**
+ * the reference count always equals the number of pointers
+ * that refer to this pbuf. This can be pointers from an application,
+ * the stack itself, or pbuf->next pointers from a chain.
+ */
+ u16_t ref;
+};
+
+
+/** Helper struct for const-correctness only.
+ * The only meaning of this one is to provide a const payload pointer
+ * for PBUF_ROM type.
+ */
+struct pbuf_rom {
+ /** next pbuf in singly linked pbuf chain */
+ struct pbuf *next;
+
+ /** pointer to the actual data in the buffer */
+ const void *payload;
+};
+
+#if LWIP_SUPPORT_CUSTOM_PBUF
+/** Prototype for a function to free a custom pbuf */
+typedef void (*pbuf_free_custom_fn)(struct pbuf *p);
+
+/** A custom pbuf: like a pbuf, but following a function pointer to free it. */
+struct pbuf_custom {
+ /** The actual pbuf */
+ struct pbuf pbuf;
+ /** This function is called when pbuf_free deallocates this pbuf(_custom) */
+ pbuf_free_custom_fn custom_free_function;
+};
+#endif /* LWIP_SUPPORT_CUSTOM_PBUF */
+
+/** 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 LWIP_TCP && TCP_QUEUE_OOSEQ && NO_SYS && PBUF_POOL_FREE_OOSEQ
+extern volatile u8_t pbuf_free_ooseq_pending;
+void pbuf_free_ooseq(void);
+/** 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)
+#else /* LWIP_TCP && TCP_QUEUE_OOSEQ && NO_SYS && PBUF_POOL_FREE_OOSEQ */
+ /* Otherwise declare an empty PBUF_CHECK_FREE_OOSEQ */
+ #define PBUF_CHECK_FREE_OOSEQ()
+#endif /* LWIP_TCP && TCP_QUEUE_OOSEQ && NO_SYS && PBUF_POOL_FREE_OOSEQ*/
+
+/* Initializes the pbuf module. This call is empty for now, but may not be in future. */
+#define pbuf_init()
+
+struct pbuf *pbuf_alloc(pbuf_layer l, u16_t length, pbuf_type type);
+#if LWIP_SUPPORT_CUSTOM_PBUF
+struct pbuf *pbuf_alloced_custom(pbuf_layer l, u16_t length, pbuf_type type,
+ struct pbuf_custom *p, void *payload_mem,
+ u16_t payload_mem_len);
+#endif /* LWIP_SUPPORT_CUSTOM_PBUF */
+void pbuf_realloc(struct pbuf *p, u16_t size);
+u8_t pbuf_header(struct pbuf *p, s16_t header_size);
+u8_t pbuf_header_force(struct pbuf *p, s16_t header_size);
+void pbuf_ref(struct pbuf *p);
+u8_t pbuf_free(struct pbuf *p);
+u16_t pbuf_clen(const struct pbuf *p);
+void pbuf_cat(struct pbuf *head, struct pbuf *tail);
+void pbuf_chain(struct pbuf *head, struct pbuf *tail);
+struct pbuf *pbuf_dechain(struct pbuf *p);
+err_t pbuf_copy(struct pbuf *p_to, const struct pbuf *p_from);
+u16_t pbuf_copy_partial(const struct pbuf *p, void *dataptr, u16_t len, u16_t offset);
+err_t pbuf_take(struct pbuf *buf, const void *dataptr, u16_t len);
+err_t pbuf_take_at(struct pbuf *buf, const void *dataptr, u16_t len, u16_t offset);
+struct pbuf *pbuf_skip(struct pbuf* in, u16_t in_offset, u16_t* out_offset);
+struct pbuf *pbuf_coalesce(struct pbuf *p, pbuf_layer layer);
+#if LWIP_CHECKSUM_ON_COPY
+err_t pbuf_fill_chksum(struct pbuf *p, u16_t start_offset, const void *dataptr,
+ u16_t len, u16_t *chksum);
+#endif /* LWIP_CHECKSUM_ON_COPY */
+#if LWIP_TCP && TCP_QUEUE_OOSEQ && LWIP_WND_SCALE
+void pbuf_split_64k(struct pbuf *p, struct pbuf **rest);
+#endif /* LWIP_TCP && TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */
+
+u8_t pbuf_get_at(const struct pbuf* p, u16_t offset);
+int pbuf_try_get_at(const struct pbuf* p, u16_t offset);
+void pbuf_put_at(struct pbuf* p, u16_t offset, u8_t data);
+u16_t pbuf_memcmp(const struct pbuf* p, u16_t offset, const void* s2, u16_t n);
+u16_t pbuf_memfind(const struct pbuf* p, const void* mem, u16_t mem_len, u16_t start_offset);
+u16_t pbuf_strstr(const struct pbuf* p, const char* substr);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_PBUF_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/pppapi.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/pppapi.h
new file mode 100644
index 0000000..b82cc8d
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/pppapi.h
@@ -0,0 +1,167 @@
+/*
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ */
+
+#ifndef __LWIP_PPPAPI_H__
+#define __LWIP_PPPAPI_H__
+
+#include "lwip/opt.h"
+
+#if LWIP_PPP_API /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/sys.h"
+#include "lwip/netif.h"
+#include "netif/ppp/ppp.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct pppapi_msg_msg {
+#if !LWIP_TCPIP_CORE_LOCKING
+ sys_sem_t sem;
+#endif /* !LWIP_TCPIP_CORE_LOCKING */
+ err_t err;
+ ppp_pcb *ppp;
+ union {
+ struct {
+ u8_t authtype;
+ const char *user;
+ const char *passwd;
+ } setauth;
+#if PPP_NOTIFY_PHASE
+ struct {
+ ppp_notify_phase_cb_fn notify_phase_cb;
+ } setnotifyphasecb;
+#endif /* PPP_NOTIFY_PHASE */
+#if PPPOS_SUPPORT
+ struct {
+ struct netif *pppif;
+ sio_fd_t fd;
+ ppp_link_status_cb_fn link_status_cb;
+ void *ctx_cb;
+ } serialcreate;
+#endif /* PPPOS_SUPPORT */
+#if PPPOE_SUPPORT
+ struct {
+ struct netif *pppif;
+ struct netif *ethif;
+ const char *service_name;
+ const char *concentrator_name;
+ ppp_link_status_cb_fn link_status_cb;
+ void *ctx_cb;
+ } ethernetcreate;
+#endif /* PPPOE_SUPPORT */
+#if PPPOL2TP_SUPPORT
+ struct {
+ struct netif *pppif;
+ struct netif *netif;
+ ip_addr_t *ipaddr;
+ u16_t port;
+#if PPPOL2TP_AUTH_SUPPORT
+ u8_t *secret;
+ u8_t secret_len;
+#endif /* PPPOL2TP_AUTH_SUPPORT */
+ ppp_link_status_cb_fn link_status_cb;
+ void *ctx_cb;
+ } l2tpcreate;
+#if LWIP_IPV6
+ struct {
+ struct netif *pppif;
+ struct netif *netif;
+ ip6_addr_t *ip6addr;
+ u16_t port;
+#if PPPOL2TP_AUTH_SUPPORT
+ u8_t *secret;
+ u8_t secret_len;
+#endif /* PPPOL2TP_AUTH_SUPPORT */
+ ppp_link_status_cb_fn link_status_cb;
+ void *ctx_cb;
+ } l2tpcreateip6;
+#endif /* LWIP_IPV6 */
+#endif /* PPPOL2TP_SUPPORT */
+ struct {
+ u16_t holdoff;
+ } connect;
+#if PPP_SERVER
+ struct {
+ struct ppp_addrs *addrs;
+ } listen;
+#endif /* PPP_SERVER */
+ struct {
+ u8_t nocarrier;
+ } close;
+ struct {
+ u8_t cmd;
+ void *arg;
+ } ioctl;
+ } msg;
+};
+
+struct pppapi_msg {
+ void (* function)(struct pppapi_msg_msg *msg);
+ struct pppapi_msg_msg msg;
+};
+
+/* API for application */
+void pppapi_set_default(ppp_pcb *pcb);
+void pppapi_set_auth(ppp_pcb *pcb, u8_t authtype, const char *user, const char *passwd);
+#if PPP_NOTIFY_PHASE
+void pppapi_set_notify_phase_callback(ppp_pcb *pcb, ppp_notify_phase_cb_fn notify_phase_cb);
+#endif /* PPP_NOTIFY_PHASE */
+#if PPPOS_SUPPORT
+ppp_pcb *pppapi_pppos_create(struct netif *pppif, sio_fd_t fd, ppp_link_status_cb_fn link_status_cb, void *ctx_cb);
+#endif /* PPPOS_SUPPORT */
+#if PPPOE_SUPPORT
+ppp_pcb *pppapi_pppoe_create(struct netif *pppif, struct netif *ethif, const char *service_name,
+ const char *concentrator_name, ppp_link_status_cb_fn link_status_cb,
+ void *ctx_cb);
+#endif /* PPPOE_SUPPORT */
+#if PPPOL2TP_SUPPORT
+ppp_pcb *pppapi_pppol2tp_create(struct netif *pppif, struct netif *netif, ip_addr_t *ipaddr, u16_t port,
+ u8_t *secret, u8_t secret_len,
+ ppp_link_status_cb_fn link_status_cb, void *ctx_cb);
+#if LWIP_IPV6
+ppp_pcb *pppapi_pppol2tp_create_ip6(struct netif *pppif, struct netif *netif, ip6_addr_t *ip6addr, u16_t port,
+ u8_t *secret, u8_t secret_len,
+ ppp_link_status_cb_fn link_status_cb, void *ctx_cb);
+#endif /* LWIP_IPV6 */
+#endif /* PPPOL2TP_SUPPORT */
+err_t pppapi_connect(ppp_pcb *pcb, u16_t holdoff);
+#if PPP_SERVER
+err_t pppapi_listen(ppp_pcb *pcb, struct ppp_addrs *addrs);
+#endif /* PPP_SERVER */
+err_t pppapi_close(ppp_pcb *pcb, u8_t nocarrier);
+err_t pppapi_free(ppp_pcb *pcb);
+err_t pppapi_ioctl(ppp_pcb *pcb, u8_t cmd, void *arg);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_PPP_API */
+
+#endif /* __LWIP_PPPAPI_H__ */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/priv/api_msg.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/priv/api_msg.h
new file mode 100644
index 0000000..f12b8b7
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/priv/api_msg.h
@@ -0,0 +1,216 @@
+/**
+ * @file
+ * netconn API lwIP internal implementations (do not use in application code)
+ */
+
+/*
+ * 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_HDR_API_MSG_H
+#define LWIP_HDR_API_MSG_H
+
+#include "lwip/opt.h"
+
+#if LWIP_NETCONN || LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */
+/* Note: Netconn API is always available when sockets are enabled -
+ * sockets are implemented on top of them */
+
+#include "lwip/arch.h"
+#include "lwip/ip_addr.h"
+#include "lwip/err.h"
+#include "lwip/sys.h"
+#include "lwip/igmp.h"
+#include "lwip/api.h"
+#include "lwip/priv/tcpip_priv.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if LWIP_MPU_COMPATIBLE
+#if LWIP_NETCONN_SEM_PER_THREAD
+#define API_MSG_M_DEF_SEM(m) *m
+#else
+#define API_MSG_M_DEF_SEM(m) API_MSG_M_DEF(m)
+#endif
+#else /* LWIP_MPU_COMPATIBLE */
+#define API_MSG_M_DEF_SEM(m) API_MSG_M_DEF(m)
+#endif /* LWIP_MPU_COMPATIBLE */
+
+/* For the netconn API, these values are use as a bitmask! */
+#define NETCONN_SHUT_RD 1
+#define NETCONN_SHUT_WR 2
+#define NETCONN_SHUT_RDWR (NETCONN_SHUT_RD | NETCONN_SHUT_WR)
+
+/* IP addresses and port numbers are expected to be in
+ * the same byte order as in the corresponding pcb.
+ */
+/** This struct includes everything that is necessary to execute a function
+ for a netconn in another thread context (mainly used to process netconns
+ in the tcpip_thread context to be thread safe). */
+struct api_msg {
+ /** The netconn which to process - always needed: it includes the semaphore
+ which is used to block the application thread until the function finished. */
+ struct netconn *conn;
+ /** The return value of the function executed in tcpip_thread. */
+ err_t err;
+ /** Depending on the executed function, one of these union members is used */
+ union {
+ /** used for lwip_netconn_do_send */
+ struct netbuf *b;
+ /** used for lwip_netconn_do_newconn */
+ struct {
+ u8_t proto;
+ } n;
+ /** used for lwip_netconn_do_bind and lwip_netconn_do_connect */
+ struct {
+ API_MSG_M_DEF_C(ip_addr_t, ipaddr);
+ u16_t port;
+ } bc;
+ /** used for lwip_netconn_do_getaddr */
+ struct {
+ ip_addr_t API_MSG_M_DEF(ipaddr);
+ u16_t API_MSG_M_DEF(port);
+ u8_t local;
+ } ad;
+ /** used for lwip_netconn_do_write */
+ struct {
+ const void *dataptr;
+ size_t len;
+ u8_t apiflags;
+#if LWIP_SO_SNDTIMEO
+ u32_t time_started;
+#endif /* LWIP_SO_SNDTIMEO */
+ } w;
+ /** used for lwip_netconn_do_recv */
+ struct {
+ u32_t len;
+ } r;
+#if LWIP_TCP
+ /** used for lwip_netconn_do_close (/shutdown) */
+ struct {
+ u8_t shut;
+#if LWIP_SO_SNDTIMEO || LWIP_SO_LINGER
+ u32_t time_started;
+#else /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */
+ u8_t polls_left;
+#endif /* LWIP_SO_SNDTIMEO || LWIP_SO_LINGER */
+ } sd;
+#endif /* LWIP_TCP */
+#if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD)
+ /** used for lwip_netconn_do_join_leave_group */
+ struct {
+ API_MSG_M_DEF_C(ip_addr_t, multiaddr);
+ API_MSG_M_DEF_C(ip_addr_t, netif_addr);
+ enum netconn_igmp join_or_leave;
+ } jl;
+#endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */
+#if TCP_LISTEN_BACKLOG
+ struct {
+ u8_t backlog;
+ } lb;
+#endif /* TCP_LISTEN_BACKLOG */
+ } msg;
+#if LWIP_NETCONN_SEM_PER_THREAD
+ sys_sem_t* op_completed_sem;
+#endif /* LWIP_NETCONN_SEM_PER_THREAD */
+};
+
+#if LWIP_NETCONN_SEM_PER_THREAD
+#define LWIP_API_MSG_SEM(msg) ((msg)->op_completed_sem)
+#else /* LWIP_NETCONN_SEM_PER_THREAD */
+#define LWIP_API_MSG_SEM(msg) (&(msg)->conn->op_completed)
+#endif /* LWIP_NETCONN_SEM_PER_THREAD */
+
+
+#if LWIP_DNS
+/** As lwip_netconn_do_gethostbyname requires more arguments but doesn't require a netconn,
+ it has its own struct (to avoid struct api_msg getting bigger than necessary).
+ lwip_netconn_do_gethostbyname must be called using tcpip_callback instead of tcpip_apimsg
+ (see netconn_gethostbyname). */
+struct dns_api_msg {
+ /** Hostname to query or dotted IP address string */
+#if LWIP_MPU_COMPATIBLE
+ char name[DNS_MAX_NAME_LENGTH];
+#else /* LWIP_MPU_COMPATIBLE */
+ const char *name;
+#endif /* LWIP_MPU_COMPATIBLE */
+ /** The resolved address is stored here */
+ ip_addr_t API_MSG_M_DEF(addr);
+#if LWIP_IPV4 && LWIP_IPV6
+ /** Type of resolve call */
+ u8_t dns_addrtype;
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+ /** This semaphore is posted when the name is resolved, the application thread
+ should wait on it. */
+ sys_sem_t API_MSG_M_DEF_SEM(sem);
+ /** Errors are given back here */
+ err_t API_MSG_M_DEF(err);
+};
+#endif /* LWIP_DNS */
+
+#if LWIP_TCP
+extern u8_t netconn_aborted;
+#endif /* LWIP_TCP */
+
+void lwip_netconn_do_newconn (void *m);
+void lwip_netconn_do_delconn (void *m);
+void lwip_netconn_do_bind (void *m);
+void lwip_netconn_do_connect (void *m);
+void lwip_netconn_do_disconnect (void *m);
+void lwip_netconn_do_listen (void *m);
+void lwip_netconn_do_send (void *m);
+void lwip_netconn_do_recv (void *m);
+#if TCP_LISTEN_BACKLOG
+void lwip_netconn_do_accepted (void *m);
+#endif /* TCP_LISTEN_BACKLOG */
+void lwip_netconn_do_write (void *m);
+void lwip_netconn_do_getaddr (void *m);
+void lwip_netconn_do_close (void *m);
+void lwip_netconn_do_shutdown (void *m);
+#if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD)
+void lwip_netconn_do_join_leave_group(void *m);
+#endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */
+
+#if LWIP_DNS
+void lwip_netconn_do_gethostbyname(void *arg);
+#endif /* LWIP_DNS */
+
+struct netconn* netconn_alloc(enum netconn_type t, netconn_callback callback);
+void netconn_free(struct netconn *conn);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_NETCONN || LWIP_SOCKET */
+
+#endif /* LWIP_HDR_API_MSG_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/priv/memp_priv.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/priv/memp_priv.h
new file mode 100644
index 0000000..f246061
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/priv/memp_priv.h
@@ -0,0 +1,183 @@
+/**
+ * @file
+ * memory pools lwIP internal implementations (do not use in application code)
+ */
+
+/*
+ * 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_HDR_MEMP_PRIV_H
+#define LWIP_HDR_MEMP_PRIV_H
+
+#include "lwip/opt.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "lwip/mem.h"
+
+#if MEMP_OVERFLOW_CHECK
+/* if MEMP_OVERFLOW_CHECK is turned on, we reserve some bytes at the beginning
+ * and at the end of each element, initialize them as 0xcd and check
+ * them later. */
+/* If MEMP_OVERFLOW_CHECK is >= 2, on every call to memp_malloc or memp_free,
+ * every single element in each pool is checked!
+ * This is VERY SLOW but also very helpful. */
+/* MEMP_SANITY_REGION_BEFORE and MEMP_SANITY_REGION_AFTER can be overridden in
+ * lwipopts.h to change the amount reserved for checking. */
+#ifndef MEMP_SANITY_REGION_BEFORE
+#define MEMP_SANITY_REGION_BEFORE 16
+#endif /* MEMP_SANITY_REGION_BEFORE*/
+#if MEMP_SANITY_REGION_BEFORE > 0
+#define MEMP_SANITY_REGION_BEFORE_ALIGNED LWIP_MEM_ALIGN_SIZE(MEMP_SANITY_REGION_BEFORE)
+#else
+#define MEMP_SANITY_REGION_BEFORE_ALIGNED 0
+#endif /* MEMP_SANITY_REGION_BEFORE*/
+#ifndef MEMP_SANITY_REGION_AFTER
+#define MEMP_SANITY_REGION_AFTER 16
+#endif /* MEMP_SANITY_REGION_AFTER*/
+#if MEMP_SANITY_REGION_AFTER > 0
+#define MEMP_SANITY_REGION_AFTER_ALIGNED LWIP_MEM_ALIGN_SIZE(MEMP_SANITY_REGION_AFTER)
+#else
+#define MEMP_SANITY_REGION_AFTER_ALIGNED 0
+#endif /* MEMP_SANITY_REGION_AFTER*/
+
+/* MEMP_SIZE: save space for struct memp and for sanity check */
+#define MEMP_SIZE (LWIP_MEM_ALIGN_SIZE(sizeof(struct memp)) + MEMP_SANITY_REGION_BEFORE_ALIGNED)
+#define MEMP_ALIGN_SIZE(x) (LWIP_MEM_ALIGN_SIZE(x) + MEMP_SANITY_REGION_AFTER_ALIGNED)
+
+#else /* MEMP_OVERFLOW_CHECK */
+
+/* No sanity checks
+ * We don't need to preserve the struct memp while not allocated, so we
+ * can save a little space and set MEMP_SIZE to 0.
+ */
+#define MEMP_SIZE 0
+#define MEMP_ALIGN_SIZE(x) (LWIP_MEM_ALIGN_SIZE(x))
+
+#endif /* MEMP_OVERFLOW_CHECK */
+
+#if !MEMP_MEM_MALLOC || MEMP_OVERFLOW_CHECK
+struct memp {
+ struct memp *next;
+#if MEMP_OVERFLOW_CHECK
+ const char *file;
+ int line;
+#endif /* MEMP_OVERFLOW_CHECK */
+};
+#endif /* !MEMP_MEM_MALLOC || MEMP_OVERFLOW_CHECK */
+
+#if MEM_USE_POOLS && MEMP_USE_CUSTOM_POOLS
+/* Use a helper type to get the start and end of the user "memory pools" for mem_malloc */
+typedef enum {
+ /* Get the first (via:
+ MEMP_POOL_HELPER_START = ((u8_t) 1*MEMP_POOL_A + 0*MEMP_POOL_B + 0*MEMP_POOL_C + 0)*/
+ MEMP_POOL_HELPER_FIRST = ((u8_t)
+#define LWIP_MEMPOOL(name,num,size,desc)
+#define LWIP_MALLOC_MEMPOOL_START 1
+#define LWIP_MALLOC_MEMPOOL(num, size) * MEMP_POOL_##size + 0
+#define LWIP_MALLOC_MEMPOOL_END
+#include "lwip/priv/memp_std.h"
+ ) ,
+ /* Get the last (via:
+ MEMP_POOL_HELPER_END = ((u8_t) 0 + MEMP_POOL_A*0 + MEMP_POOL_B*0 + MEMP_POOL_C*1) */
+ MEMP_POOL_HELPER_LAST = ((u8_t)
+#define LWIP_MEMPOOL(name,num,size,desc)
+#define LWIP_MALLOC_MEMPOOL_START
+#define LWIP_MALLOC_MEMPOOL(num, size) 0 + MEMP_POOL_##size *
+#define LWIP_MALLOC_MEMPOOL_END 1
+#include "lwip/priv/memp_std.h"
+ )
+} memp_pool_helper_t;
+
+/* The actual start and stop values are here (cast them over)
+ We use this helper type and these defines so we can avoid using const memp_t values */
+#define MEMP_POOL_FIRST ((memp_t) MEMP_POOL_HELPER_FIRST)
+#define MEMP_POOL_LAST ((memp_t) MEMP_POOL_HELPER_LAST)
+#endif /* MEM_USE_POOLS && MEMP_USE_CUSTOM_POOLS */
+
+/** Memory pool descriptor */
+struct memp_desc {
+#if defined(LWIP_DEBUG) || MEMP_OVERFLOW_CHECK || LWIP_STATS_DISPLAY
+ /** Textual description */
+ const char *desc;
+#endif /* LWIP_DEBUG || MEMP_OVERFLOW_CHECK || LWIP_STATS_DISPLAY */
+#if MEMP_STATS
+ /** Statistics */
+ struct stats_mem *stats;
+#endif
+
+ /** Element size */
+ u16_t size;
+
+#if !MEMP_MEM_MALLOC
+ /** Number of elements */
+ u16_t num;
+
+ /** Base address */
+ u8_t *base;
+
+ /** First free element of each pool. Elements form a linked list. */
+ struct memp **tab;
+#endif /* MEMP_MEM_MALLOC */
+};
+
+#if defined(LWIP_DEBUG) || MEMP_OVERFLOW_CHECK || LWIP_STATS_DISPLAY
+#define DECLARE_LWIP_MEMPOOL_DESC(desc) (desc),
+#else
+#define DECLARE_LWIP_MEMPOOL_DESC(desc)
+#endif
+
+#if MEMP_STATS
+#define LWIP_MEMPOOL_DECLARE_STATS_INSTANCE(name) static struct stats_mem name;
+#define LWIP_MEMPOOL_DECLARE_STATS_REFERENCE(name) &name,
+#else
+#define LWIP_MEMPOOL_DECLARE_STATS_INSTANCE(name)
+#define LWIP_MEMPOOL_DECLARE_STATS_REFERENCE(name)
+#endif
+
+void memp_init_pool(const struct memp_desc *desc);
+
+#if MEMP_OVERFLOW_CHECK
+void *memp_malloc_pool_fn(const struct memp_desc* desc, const char* file, const int line);
+#define memp_malloc_pool(d) memp_malloc_pool_fn((d), __FILE__, __LINE__)
+#else
+void *memp_malloc_pool(const struct memp_desc *desc);
+#endif
+void memp_free_pool(const struct memp_desc* desc, void *mem);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_MEMP_PRIV_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/priv/memp_std.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/priv/memp_std.h
new file mode 100644
index 0000000..ce9fd50
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/priv/memp_std.h
@@ -0,0 +1,146 @@
+/**
+ * @file
+ * lwIP internal memory pools (do not use in application code)
+ * This file is deliberately included multiple times: once with empty
+ * definition of LWIP_MEMPOOL() to handle all includes and multiple times
+ * to build up various lists of mem pools.
+ */
+
+/*
+ * SETUP: Make sure we define everything we will need.
+ *
+ * We have create three types of pools:
+ * 1) MEMPOOL - standard pools
+ * 2) MALLOC_MEMPOOL - to be used by mem_malloc in mem.c
+ * 3) PBUF_MEMPOOL - a mempool of pbuf's, so include space for the pbuf struct
+ *
+ * If the include'r doesn't require any special treatment of each of the types
+ * above, then will declare #2 & #3 to be just standard mempools.
+ */
+#ifndef LWIP_MALLOC_MEMPOOL
+/* This treats "malloc pools" just like any other pool.
+ The pools are a little bigger to provide 'size' as the amount of user data. */
+#define LWIP_MALLOC_MEMPOOL(num, size) LWIP_MEMPOOL(POOL_##size, num, (size + LWIP_MEM_ALIGN_SIZE(sizeof(struct memp_malloc_helper))), "MALLOC_"#size)
+#define LWIP_MALLOC_MEMPOOL_START
+#define LWIP_MALLOC_MEMPOOL_END
+#endif /* LWIP_MALLOC_MEMPOOL */
+
+#ifndef LWIP_PBUF_MEMPOOL
+/* This treats "pbuf pools" just like any other pool.
+ * Allocates buffers for a pbuf struct AND a payload size */
+#define LWIP_PBUF_MEMPOOL(name, num, payload, desc) LWIP_MEMPOOL(name, num, (MEMP_ALIGN_SIZE(sizeof(struct pbuf)) + MEMP_ALIGN_SIZE(payload)), desc)
+#endif /* LWIP_PBUF_MEMPOOL */
+
+
+/*
+ * A list of internal pools used by LWIP.
+ *
+ * LWIP_MEMPOOL(pool_name, number_elements, element_size, pool_description)
+ * creates a pool name MEMP_pool_name. description is used in stats.c
+ */
+#if LWIP_RAW
+LWIP_MEMPOOL(RAW_PCB, MEMP_NUM_RAW_PCB, sizeof(struct raw_pcb), "RAW_PCB")
+#endif /* LWIP_RAW */
+
+#if LWIP_UDP
+LWIP_MEMPOOL(UDP_PCB, MEMP_NUM_UDP_PCB, sizeof(struct udp_pcb), "UDP_PCB")
+#endif /* LWIP_UDP */
+
+#if LWIP_TCP
+LWIP_MEMPOOL(TCP_PCB, MEMP_NUM_TCP_PCB, sizeof(struct tcp_pcb), "TCP_PCB")
+LWIP_MEMPOOL(TCP_PCB_LISTEN, MEMP_NUM_TCP_PCB_LISTEN, sizeof(struct tcp_pcb_listen), "TCP_PCB_LISTEN")
+LWIP_MEMPOOL(TCP_SEG, MEMP_NUM_TCP_SEG, sizeof(struct tcp_seg), "TCP_SEG")
+#endif /* LWIP_TCP */
+
+#if LWIP_IPV4 && IP_REASSEMBLY
+LWIP_MEMPOOL(REASSDATA, MEMP_NUM_REASSDATA, sizeof(struct ip_reassdata), "REASSDATA")
+#endif /* LWIP_IPV4 && IP_REASSEMBLY */
+#if (IP_FRAG && !LWIP_NETIF_TX_SINGLE_PBUF) || (LWIP_IPV6 && LWIP_IPV6_FRAG)
+LWIP_MEMPOOL(FRAG_PBUF, MEMP_NUM_FRAG_PBUF, sizeof(struct pbuf_custom_ref),"FRAG_PBUF")
+#endif /* IP_FRAG && !LWIP_NETIF_TX_SINGLE_PBUF || (LWIP_IPV6 && LWIP_IPV6_FRAG) */
+
+#if LWIP_NETCONN || LWIP_SOCKET
+LWIP_MEMPOOL(NETBUF, MEMP_NUM_NETBUF, sizeof(struct netbuf), "NETBUF")
+LWIP_MEMPOOL(NETCONN, MEMP_NUM_NETCONN, sizeof(struct netconn), "NETCONN")
+#endif /* LWIP_NETCONN || LWIP_SOCKET */
+
+#if NO_SYS==0
+LWIP_MEMPOOL(TCPIP_MSG_API, MEMP_NUM_TCPIP_MSG_API, sizeof(struct tcpip_msg), "TCPIP_MSG_API")
+#if LWIP_MPU_COMPATIBLE
+LWIP_MEMPOOL(API_MSG, MEMP_NUM_API_MSG, sizeof(struct api_msg), "API_MSG")
+#if LWIP_DNS
+LWIP_MEMPOOL(DNS_API_MSG, MEMP_NUM_DNS_API_MSG, sizeof(struct dns_api_msg), "DNS_API_MSG")
+#endif
+#if LWIP_SOCKET && !LWIP_TCPIP_CORE_LOCKING
+LWIP_MEMPOOL(SOCKET_SETGETSOCKOPT_DATA, MEMP_NUM_SOCKET_SETGETSOCKOPT_DATA, sizeof(struct lwip_setgetsockopt_data), "SOCKET_SETGETSOCKOPT_DATA")
+#endif
+#if LWIP_NETIF_API
+LWIP_MEMPOOL(NETIFAPI_MSG, MEMP_NUM_NETIFAPI_MSG, sizeof(struct netifapi_msg), "NETIFAPI_MSG")
+#endif
+#endif /* LWIP_MPU_COMPATIBLE */
+#if !LWIP_TCPIP_CORE_LOCKING_INPUT
+LWIP_MEMPOOL(TCPIP_MSG_INPKT,MEMP_NUM_TCPIP_MSG_INPKT, sizeof(struct tcpip_msg), "TCPIP_MSG_INPKT")
+#endif /* !LWIP_TCPIP_CORE_LOCKING_INPUT */
+#endif /* NO_SYS==0 */
+
+#if LWIP_IPV4 && LWIP_ARP && ARP_QUEUEING
+LWIP_MEMPOOL(ARP_QUEUE, MEMP_NUM_ARP_QUEUE, sizeof(struct etharp_q_entry), "ARP_QUEUE")
+#endif /* LWIP_IPV4 && LWIP_ARP && ARP_QUEUEING */
+
+#if LWIP_IGMP
+LWIP_MEMPOOL(IGMP_GROUP, MEMP_NUM_IGMP_GROUP, sizeof(struct igmp_group), "IGMP_GROUP")
+#endif /* LWIP_IGMP */
+
+#if LWIP_TIMERS && !LWIP_TIMERS_CUSTOM
+LWIP_MEMPOOL(SYS_TIMEOUT, MEMP_NUM_SYS_TIMEOUT, sizeof(struct sys_timeo), "SYS_TIMEOUT")
+#endif /* LWIP_TIMERS && !LWIP_TIMERS_CUSTOM */
+
+#if LWIP_DNS && LWIP_SOCKET
+LWIP_MEMPOOL(NETDB, MEMP_NUM_NETDB, NETDB_ELEM_SIZE, "NETDB")
+#endif /* LWIP_DNS && LWIP_SOCKET */
+#if LWIP_DNS && DNS_LOCAL_HOSTLIST && DNS_LOCAL_HOSTLIST_IS_DYNAMIC
+LWIP_MEMPOOL(LOCALHOSTLIST, MEMP_NUM_LOCALHOSTLIST, LOCALHOSTLIST_ELEM_SIZE, "LOCALHOSTLIST")
+#endif /* LWIP_DNS && DNS_LOCAL_HOSTLIST && DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
+
+#if LWIP_IPV6 && LWIP_ND6_QUEUEING
+LWIP_MEMPOOL(ND6_QUEUE, MEMP_NUM_ND6_QUEUE, sizeof(struct nd6_q_entry), "ND6_QUEUE")
+#endif /* LWIP_IPV6 && LWIP_ND6_QUEUEING */
+
+#if LWIP_IPV6 && LWIP_IPV6_REASS
+LWIP_MEMPOOL(IP6_REASSDATA, MEMP_NUM_REASSDATA, sizeof(struct ip6_reassdata), "IP6_REASSDATA")
+#endif /* LWIP_IPV6 && LWIP_IPV6_REASS */
+
+#if LWIP_IPV6 && LWIP_IPV6_MLD
+LWIP_MEMPOOL(MLD6_GROUP, MEMP_NUM_MLD6_GROUP, sizeof(struct mld_group), "MLD6_GROUP")
+#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */
+
+
+/*
+ * A list of pools of pbuf's used by LWIP.
+ *
+ * LWIP_PBUF_MEMPOOL(pool_name, number_elements, pbuf_payload_size, pool_description)
+ * creates a pool name MEMP_pool_name. description is used in stats.c
+ * This allocates enough space for the pbuf struct and a payload.
+ * (Example: pbuf_payload_size=0 allocates only size for the struct)
+ */
+LWIP_PBUF_MEMPOOL(PBUF, MEMP_NUM_PBUF, 0, "PBUF_REF/ROM")
+LWIP_PBUF_MEMPOOL(PBUF_POOL, PBUF_POOL_SIZE, PBUF_POOL_BUFSIZE, "PBUF_POOL")
+
+
+/*
+ * Allow for user-defined pools; this must be explicitly set in lwipopts.h
+ * since the default is to NOT look for lwippools.h
+ */
+#if MEMP_USE_CUSTOM_POOLS
+#include "lwippools.h"
+#endif /* MEMP_USE_CUSTOM_POOLS */
+
+/*
+ * REQUIRED CLEANUP: Clear up so we don't get "multiply defined" error later
+ * (#undef is ignored for something that is not defined)
+ */
+#undef LWIP_MEMPOOL
+#undef LWIP_MALLOC_MEMPOOL
+#undef LWIP_MALLOC_MEMPOOL_START
+#undef LWIP_MALLOC_MEMPOOL_END
+#undef LWIP_PBUF_MEMPOOL
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/priv/nd6_priv.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/priv/nd6_priv.h
new file mode 100644
index 0000000..4bda0b7
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/priv/nd6_priv.h
@@ -0,0 +1,144 @@
+/**
+ * @file
+ *
+ * Neighbor discovery and stateless address autoconfiguration for IPv6.
+ * Aims to be compliant with RFC 4861 (Neighbor discovery) and RFC 4862
+ * (Address autoconfiguration).
+ */
+
+/*
+ * Copyright (c) 2010 Inico Technologies Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Ivan Delamer <delamer@inicotech.com>
+ *
+ *
+ * Please coordinate changes and requests with Ivan Delamer
+ * <delamer@inicotech.com>
+ */
+
+#ifndef LWIP_HDR_ND6_PRIV_H
+#define LWIP_HDR_ND6_PRIV_H
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/pbuf.h"
+#include "lwip/ip6_addr.h"
+#include "lwip/netif.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if LWIP_ND6_QUEUEING
+/** struct for queueing outgoing packets for unknown address
+ * defined here to be accessed by memp.h
+ */
+struct nd6_q_entry {
+ struct nd6_q_entry *next;
+ struct pbuf *p;
+};
+#endif /* LWIP_ND6_QUEUEING */
+
+/** Struct for tables. */
+struct nd6_neighbor_cache_entry {
+ ip6_addr_t next_hop_address;
+ struct netif *netif;
+ u8_t lladdr[NETIF_MAX_HWADDR_LEN];
+ /*u32_t pmtu;*/
+#if LWIP_ND6_QUEUEING
+ /** Pointer to queue of pending outgoing packets on this entry. */
+ struct nd6_q_entry *q;
+#else /* LWIP_ND6_QUEUEING */
+ /** Pointer to a single pending outgoing packet on this entry. */
+ struct pbuf *q;
+#endif /* LWIP_ND6_QUEUEING */
+ u8_t state;
+ u8_t isrouter;
+ union {
+ u32_t reachable_time; /* in ms since value may originate from network packet */
+ u32_t delay_time; /* ticks (ND6_TMR_INTERVAL) */
+ u32_t probes_sent;
+ u32_t stale_time; /* ticks (ND6_TMR_INTERVAL) */
+ } counter;
+};
+
+struct nd6_destination_cache_entry {
+ ip6_addr_t destination_addr;
+ ip6_addr_t next_hop_addr;
+ u16_t pmtu;
+ u32_t age;
+};
+
+struct nd6_prefix_list_entry {
+ ip6_addr_t prefix;
+ struct netif *netif;
+ u32_t invalidation_timer; /* in ms since value may originate from network packet */
+#if LWIP_IPV6_AUTOCONFIG
+ u8_t flags;
+#define ND6_PREFIX_AUTOCONFIG_AUTONOMOUS 0x01
+#define ND6_PREFIX_AUTOCONFIG_ADDRESS_GENERATED 0x02
+#define ND6_PREFIX_AUTOCONFIG_ADDRESS_DUPLICATE 0x04
+#endif /* LWIP_IPV6_AUTOCONFIG */
+};
+
+struct nd6_router_list_entry {
+ struct nd6_neighbor_cache_entry *neighbor_entry;
+ u32_t invalidation_timer; /* in ms since value may originate from network packet */
+ u8_t flags;
+};
+
+enum nd6_neighbor_cache_entry_state {
+ ND6_NO_ENTRY = 0,
+ ND6_INCOMPLETE,
+ ND6_REACHABLE,
+ ND6_STALE,
+ ND6_DELAY,
+ ND6_PROBE
+};
+
+/* Router tables. */
+/* @todo make these static? and entries accessible through API? */
+extern struct nd6_neighbor_cache_entry neighbor_cache[];
+extern struct nd6_destination_cache_entry destination_cache[];
+extern struct nd6_prefix_list_entry prefix_list[];
+extern struct nd6_router_list_entry default_router_list[];
+
+/* Default values, can be updated by a RA message. */
+extern u32_t reachable_time;
+extern u32_t retrans_timer;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_IPV6 */
+
+#endif /* LWIP_HDR_ND6_PRIV_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/priv/tcp_priv.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/priv/tcp_priv.h
new file mode 100644
index 0000000..73e8967
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/priv/tcp_priv.h
@@ -0,0 +1,507 @@
+/**
+ * @file
+ * TCP internal implementations (do not use in application code)
+ */
+
+/*
+ * 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_HDR_TCP_PRIV_H
+#define LWIP_HDR_TCP_PRIV_H
+
+#include "lwip/opt.h"
+
+#if LWIP_TCP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/tcp.h"
+#include "lwip/mem.h"
+#include "lwip/pbuf.h"
+#include "lwip/ip.h"
+#include "lwip/icmp.h"
+#include "lwip/err.h"
+#include "lwip/ip6.h"
+#include "lwip/ip6_addr.h"
+#include "lwip/prot/tcp.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Functions for interfacing with TCP: */
+
+/* Lower layer interface to TCP: */
+void tcp_init (void); /* Initialize this module. */
+void tcp_tmr (void); /* Must be called every
+ TCP_TMR_INTERVAL
+ ms. (Typically 250 ms). */
+/* It is also possible to call these two functions at the right
+ intervals (instead of calling tcp_tmr()). */
+void tcp_slowtmr (void);
+void tcp_fasttmr (void);
+
+/* Call this from a netif driver (watch out for threading issues!) that has
+ returned a memory error on transmit and now has free buffers to send more.
+ This iterates all active pcbs that had an error and tries to call
+ tcp_output, so use this with care as it might slow down the system. */
+void tcp_txnow (void);
+
+/* Only used by IP to pass a TCP segment to TCP: */
+void tcp_input (struct pbuf *p, struct netif *inp);
+/* Used within the TCP code only: */
+struct tcp_pcb * tcp_alloc (u8_t prio);
+void tcp_abandon (struct tcp_pcb *pcb, int reset);
+err_t tcp_send_empty_ack(struct tcp_pcb *pcb);
+void tcp_rexmit (struct tcp_pcb *pcb);
+void tcp_rexmit_rto (struct tcp_pcb *pcb);
+void tcp_rexmit_fast (struct tcp_pcb *pcb);
+u32_t tcp_update_rcv_ann_wnd(struct tcp_pcb *pcb);
+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
+ * segments as possible. Only send if
+ * - no previously transmitted data on the connection remains unacknowledged or
+ * - the TF_NODELAY flag is set (nagle algorithm turned off for this pcb) or
+ * - the only unsent segment is at least pcb->mss bytes long (or there is more
+ * than one unsent segment - with lwIP, this can happen although unsent->len < mss)
+ * - or if we are in fast-retransmit (TF_INFR)
+ */
+#define tcp_do_output_nagle(tpcb) ((((tpcb)->unacked == NULL) || \
+ ((tpcb)->flags & (TF_NODELAY | TF_INFR)) || \
+ (((tpcb)->unsent != NULL) && (((tpcb)->unsent->next != NULL) || \
+ ((tpcb)->unsent->len >= (tpcb)->mss))) || \
+ ((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)((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))
+#endif
+#define TCP_SEQ_BETWEEN(a,b,c) (TCP_SEQ_GEQ(a,b) && TCP_SEQ_LEQ(a,c))
+
+#ifndef TCP_TMR_INTERVAL
+#define TCP_TMR_INTERVAL 250 /* The TCP timer interval in milliseconds. */
+#endif /* TCP_TMR_INTERVAL */
+
+#ifndef TCP_FAST_INTERVAL
+#define TCP_FAST_INTERVAL TCP_TMR_INTERVAL /* the fine grained timeout in milliseconds */
+#endif /* TCP_FAST_INTERVAL */
+
+#ifndef TCP_SLOW_INTERVAL
+#define TCP_SLOW_INTERVAL (2*TCP_TMR_INTERVAL) /* the coarse grained timeout in milliseconds */
+#endif /* TCP_SLOW_INTERVAL */
+
+#define TCP_FIN_WAIT_TIMEOUT 20000 /* milliseconds */
+#define TCP_SYN_RCVD_TIMEOUT 20000 /* milliseconds */
+
+#define TCP_OOSEQ_TIMEOUT 6U /* x RTO */
+
+#ifndef TCP_MSL
+#define TCP_MSL 60000UL /* The maximum segment lifetime in milliseconds */
+#endif
+
+/* Keepalive values, compliant with RFC 1122. Don't change this unless you know what you're doing */
+#ifndef TCP_KEEPIDLE_DEFAULT
+#define TCP_KEEPIDLE_DEFAULT 7200000UL /* Default KEEPALIVE timer in milliseconds */
+#endif
+
+#ifndef TCP_KEEPINTVL_DEFAULT
+#define TCP_KEEPINTVL_DEFAULT 75000UL /* Default Time between KEEPALIVE probes in milliseconds */
+#endif
+
+#ifndef TCP_KEEPCNT_DEFAULT
+#define TCP_KEEPCNT_DEFAULT 9U /* Default Counter for KEEPALIVE probes */
+#endif
+
+#define TCP_MAXIDLE TCP_KEEPCNT_DEFAULT * TCP_KEEPINTVL_DEFAULT /* Maximum KEEPALIVE probe time */
+
+#define TCP_TCPLEN(seg) ((seg)->len + (((TCPH_FLAGS((seg)->tcphdr) & (TCP_FIN | TCP_SYN)) != 0) ? 1U : 0U))
+
+/** Flags used on input processing, not on pcb->flags
+*/
+#define TF_RESET (u8_t)0x08U /* Connection was reset. */
+#define TF_CLOSED (u8_t)0x10U /* Connection was successfully closed. */
+#define TF_GOT_FIN (u8_t)0x20U /* Connection was closed by the remote end. */
+
+
+#if LWIP_EVENT_API
+
+#define TCP_EVENT_ACCEPT(lpcb,pcb,arg,err,ret) ret = lwip_tcp_event(arg, (pcb),\
+ LWIP_EVENT_ACCEPT, NULL, 0, err)
+#define TCP_EVENT_SENT(pcb,space,ret) ret = lwip_tcp_event((pcb)->callback_arg, (pcb),\
+ LWIP_EVENT_SENT, NULL, space, ERR_OK)
+#define TCP_EVENT_RECV(pcb,p,err,ret) ret = lwip_tcp_event((pcb)->callback_arg, (pcb),\
+ LWIP_EVENT_RECV, (p), 0, (err))
+#define TCP_EVENT_CLOSED(pcb,ret) ret = lwip_tcp_event((pcb)->callback_arg, (pcb),\
+ LWIP_EVENT_RECV, NULL, 0, ERR_OK)
+#define TCP_EVENT_CONNECTED(pcb,err,ret) ret = lwip_tcp_event((pcb)->callback_arg, (pcb),\
+ LWIP_EVENT_CONNECTED, NULL, 0, (err))
+#define TCP_EVENT_POLL(pcb,ret) do { if ((pcb)->state != SYN_RCVD) { \
+ ret = lwip_tcp_event((pcb)->callback_arg, (pcb), LWIP_EVENT_POLL, NULL, 0, ERR_OK); \
+ } else { \
+ ret = ERR_ARG; } } while(0)
+#define TCP_EVENT_ERR(last_state,errf,arg,err) do { if (last_state != SYN_RCVD) { \
+ lwip_tcp_event((arg), NULL, LWIP_EVENT_ERR, NULL, 0, (err)); } } while(0)
+
+#else /* LWIP_EVENT_API */
+
+#define TCP_EVENT_ACCEPT(lpcb,pcb,arg,err,ret) \
+ do { \
+ if((lpcb)->accept != NULL) \
+ (ret) = (lpcb)->accept((arg),(pcb),(err)); \
+ else (ret) = ERR_ARG; \
+ } while (0)
+
+#define TCP_EVENT_SENT(pcb,space,ret) \
+ do { \
+ if((pcb)->sent != NULL) \
+ (ret) = (pcb)->sent((pcb)->callback_arg,(pcb),(space)); \
+ else (ret) = ERR_OK; \
+ } while (0)
+
+#define TCP_EVENT_RECV(pcb,p,err,ret) \
+ do { \
+ if((pcb)->recv != NULL) { \
+ (ret) = (pcb)->recv((pcb)->callback_arg,(pcb),(p),(err));\
+ } else { \
+ (ret) = tcp_recv_null(NULL, (pcb), (p), (err)); \
+ } \
+ } while (0)
+
+#define TCP_EVENT_CLOSED(pcb,ret) \
+ do { \
+ if(((pcb)->recv != NULL)) { \
+ (ret) = (pcb)->recv((pcb)->callback_arg,(pcb),NULL,ERR_OK);\
+ } else { \
+ (ret) = ERR_OK; \
+ } \
+ } while (0)
+
+#define TCP_EVENT_CONNECTED(pcb,err,ret) \
+ do { \
+ if((pcb)->connected != NULL) \
+ (ret) = (pcb)->connected((pcb)->callback_arg,(pcb),(err)); \
+ else (ret) = ERR_OK; \
+ } while (0)
+
+#define TCP_EVENT_POLL(pcb,ret) \
+ do { \
+ if((pcb)->poll != NULL) \
+ (ret) = (pcb)->poll((pcb)->callback_arg,(pcb)); \
+ else (ret) = ERR_OK; \
+ } while (0)
+
+#define TCP_EVENT_ERR(last_state,errf,arg,err) \
+ do { \
+ LWIP_UNUSED_ARG(last_state); \
+ if((errf) != NULL) \
+ (errf)((arg),(err)); \
+ } while (0)
+
+#endif /* LWIP_EVENT_API */
+
+/** Enabled extra-check for TCP_OVERSIZE if LWIP_DEBUG is enabled */
+#if TCP_OVERSIZE && defined(LWIP_DEBUG)
+#define TCP_OVERSIZE_DBGCHECK 1
+#else
+#define TCP_OVERSIZE_DBGCHECK 0
+#endif
+
+/** Don't generate checksum on copy if CHECKSUM_GEN_TCP is disabled */
+#define TCP_CHECKSUM_ON_COPY (LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_TCP)
+
+/* This structure represents a TCP segment on the unsent, unacked and ooseq queues */
+struct tcp_seg {
+ struct tcp_seg *next; /* used when putting segments on a queue */
+ struct pbuf *p; /* buffer containing data + TCP header */
+ u16_t len; /* the TCP length of this segment */
+#if TCP_OVERSIZE_DBGCHECK
+ u16_t oversize_left; /* Extra bytes available at the end of the last
+ pbuf in unsent (used for asserting vs.
+ tcp_pcb.unsent_oversize only) */
+#endif /* TCP_OVERSIZE_DBGCHECK */
+#if TCP_CHECKSUM_ON_COPY
+ u16_t chksum;
+ u8_t chksum_swapped;
+#endif /* TCP_CHECKSUM_ON_COPY */
+ u8_t flags;
+#define TF_SEG_OPTS_MSS (u8_t)0x01U /* Include MSS option. */
+#define TF_SEG_OPTS_TS (u8_t)0x02U /* Include timestamp option. */
+#define TF_SEG_DATA_CHECKSUMMED (u8_t)0x04U /* ALL data (not the header) is
+ checksummed into 'chksum' */
+#define TF_SEG_OPTS_WND_SCALE (u8_t)0x08U /* Include WND SCALE option */
+ struct tcp_hdr *tcphdr; /* the TCP header */
+};
+
+#define LWIP_TCP_OPT_EOL 0
+#define LWIP_TCP_OPT_NOP 1
+#define LWIP_TCP_OPT_MSS 2
+#define LWIP_TCP_OPT_WS 3
+#define LWIP_TCP_OPT_TS 8
+
+#define LWIP_TCP_OPT_LEN_MSS 4
+#if LWIP_TCP_TIMESTAMPS
+#define LWIP_TCP_OPT_LEN_TS 10
+#define LWIP_TCP_OPT_LEN_TS_OUT 12 /* aligned for output (includes NOP padding) */
+#else
+#define LWIP_TCP_OPT_LEN_TS_OUT 0
+#endif
+#if LWIP_WND_SCALE
+#define LWIP_TCP_OPT_LEN_WS 3
+#define LWIP_TCP_OPT_LEN_WS_OUT 4 /* aligned for output (includes NOP padding) */
+#else
+#define LWIP_TCP_OPT_LEN_WS_OUT 0
+#endif
+
+#define LWIP_TCP_OPT_LENGTH(flags) \
+ (flags & TF_SEG_OPTS_MSS ? LWIP_TCP_OPT_LEN_MSS : 0) + \
+ (flags & TF_SEG_OPTS_TS ? LWIP_TCP_OPT_LEN_TS_OUT : 0) + \
+ (flags & TF_SEG_OPTS_WND_SCALE ? LWIP_TCP_OPT_LEN_WS_OUT : 0)
+
+/** This returns a TCP header option for MSS in an u32_t */
+#define TCP_BUILD_MSS_OPTION(mss) lwip_htonl(0x02040000 | ((mss) & 0xFFFF))
+
+#if LWIP_WND_SCALE
+#define TCPWNDSIZE_F U32_F
+#define TCPWND_MAX 0xFFFFFFFFU
+#define TCPWND_CHECK16(x) LWIP_ASSERT("window size > 0xFFFF", (x) <= 0xFFFF)
+#define TCPWND_MIN16(x) ((u16_t)LWIP_MIN((x), 0xFFFF))
+#else /* LWIP_WND_SCALE */
+#define TCPWNDSIZE_F U16_F
+#define TCPWND_MAX 0xFFFFU
+#define TCPWND_CHECK16(x)
+#define TCPWND_MIN16(x) x
+#endif /* LWIP_WND_SCALE */
+
+/* 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. */
+ struct tcp_pcb_listen *listen_pcbs;
+ struct tcp_pcb *pcbs;
+};
+extern struct tcp_pcb *tcp_bound_pcbs;
+extern union tcp_listen_pcbs_t tcp_listen_pcbs;
+extern struct tcp_pcb *tcp_active_pcbs; /* List of all TCP PCBs that are in a
+ state in which they accept or send
+ data. */
+extern struct tcp_pcb *tcp_tw_pcbs; /* List of all TCP PCBs in TIME-WAIT. */
+
+#define NUM_TCP_PCB_LISTS_NO_TIME_WAIT 3
+#define NUM_TCP_PCB_LISTS 4
+extern struct tcp_pcb ** const tcp_pcb_lists[NUM_TCP_PCB_LISTS];
+
+/* Axioms about the above lists:
+ 1) Every TCP PCB that is not CLOSED is in one of the lists.
+ 2) A PCB is only in one of the lists.
+ 3) All PCBs in the tcp_listen_pcbs list is in LISTEN state.
+ 4) All PCBs in the tcp_tw_pcbs list is in TIME-WAIT state.
+*/
+/* Define two macros, TCP_REG and TCP_RMV that registers a TCP PCB
+ with a PCB list or removes a PCB from a list, respectively. */
+#ifndef TCP_DEBUG_PCB_LISTS
+#define TCP_DEBUG_PCB_LISTS 0
+#endif
+#if TCP_DEBUG_PCB_LISTS
+#define TCP_REG(pcbs, npcb) do {\
+ struct tcp_pcb *tcp_tmp_pcb; \
+ LWIP_DEBUGF(TCP_DEBUG, ("TCP_REG %p local port %d\n", (npcb), (npcb)->local_port)); \
+ for (tcp_tmp_pcb = *(pcbs); \
+ tcp_tmp_pcb != NULL; \
+ tcp_tmp_pcb = tcp_tmp_pcb->next) { \
+ LWIP_ASSERT("TCP_REG: already registered\n", tcp_tmp_pcb != (npcb)); \
+ } \
+ LWIP_ASSERT("TCP_REG: pcb->state != CLOSED", ((pcbs) == &tcp_bound_pcbs) || ((npcb)->state != CLOSED)); \
+ (npcb)->next = *(pcbs); \
+ LWIP_ASSERT("TCP_REG: npcb->next != npcb", (npcb)->next != (npcb)); \
+ *(pcbs) = (npcb); \
+ LWIP_ASSERT("TCP_RMV: tcp_pcbs sane", tcp_pcbs_sane()); \
+ tcp_timer_needed(); \
+ } while(0)
+#define TCP_RMV(pcbs, npcb) do { \
+ struct tcp_pcb *tcp_tmp_pcb; \
+ LWIP_ASSERT("TCP_RMV: pcbs != NULL", *(pcbs) != NULL); \
+ LWIP_DEBUGF(TCP_DEBUG, ("TCP_RMV: removing %p from %p\n", (npcb), *(pcbs))); \
+ if(*(pcbs) == (npcb)) { \
+ *(pcbs) = (*pcbs)->next; \
+ } else for (tcp_tmp_pcb = *(pcbs); tcp_tmp_pcb != NULL; tcp_tmp_pcb = tcp_tmp_pcb->next) { \
+ if(tcp_tmp_pcb->next == (npcb)) { \
+ tcp_tmp_pcb->next = (npcb)->next; \
+ break; \
+ } \
+ } \
+ (npcb)->next = NULL; \
+ LWIP_ASSERT("TCP_RMV: tcp_pcbs sane", tcp_pcbs_sane()); \
+ LWIP_DEBUGF(TCP_DEBUG, ("TCP_RMV: removed %p from %p\n", (npcb), *(pcbs))); \
+ } while(0)
+
+#else /* LWIP_DEBUG */
+
+#define TCP_REG(pcbs, npcb) \
+ do { \
+ (npcb)->next = *pcbs; \
+ *(pcbs) = (npcb); \
+ tcp_timer_needed(); \
+ } while (0)
+
+#define TCP_RMV(pcbs, npcb) \
+ do { \
+ if(*(pcbs) == (npcb)) { \
+ (*(pcbs)) = (*pcbs)->next; \
+ } \
+ else { \
+ struct tcp_pcb *tcp_tmp_pcb; \
+ for (tcp_tmp_pcb = *pcbs; \
+ tcp_tmp_pcb != NULL; \
+ tcp_tmp_pcb = tcp_tmp_pcb->next) { \
+ if(tcp_tmp_pcb->next == (npcb)) { \
+ tcp_tmp_pcb->next = (npcb)->next; \
+ break; \
+ } \
+ } \
+ } \
+ (npcb)->next = NULL; \
+ } while(0)
+
+#endif /* LWIP_DEBUG */
+
+#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);
+void tcp_pcb_purge(struct tcp_pcb *pcb);
+void tcp_pcb_remove(struct tcp_pcb **pcblist, struct tcp_pcb *pcb);
+
+void tcp_segs_free(struct tcp_seg *seg);
+void tcp_seg_free(struct tcp_seg *seg);
+struct tcp_seg *tcp_seg_copy(struct tcp_seg *seg);
+
+#define tcp_ack(pcb) \
+ do { \
+ if((pcb)->flags & TF_ACK_DELAY) { \
+ (pcb)->flags &= ~TF_ACK_DELAY; \
+ (pcb)->flags |= TF_ACK_NOW; \
+ } \
+ else { \
+ (pcb)->flags |= TF_ACK_DELAY; \
+ } \
+ } while (0)
+
+#define tcp_ack_now(pcb) \
+ do { \
+ (pcb)->flags |= TF_ACK_NOW; \
+ } while (0)
+
+err_t tcp_send_fin(struct tcp_pcb *pcb);
+err_t tcp_enqueue_flags(struct tcp_pcb *pcb, u8_t flags);
+
+void tcp_rexmit_seg(struct tcp_pcb *pcb, struct tcp_seg *seg);
+
+void tcp_rst(u32_t seqno, u32_t ackno,
+ const ip_addr_t *local_ip, const ip_addr_t *remote_ip,
+ u16_t local_port, u16_t remote_port);
+
+u32_t tcp_next_iss(struct tcp_pcb *pcb);
+
+err_t tcp_keepalive(struct tcp_pcb *pcb);
+err_t tcp_zero_window_probe(struct tcp_pcb *pcb);
+void tcp_trigger_input_pcb_close(void);
+
+#if TCP_CALCULATE_EFF_SEND_MSS
+u16_t tcp_eff_send_mss_impl(u16_t sendmss, const ip_addr_t *dest
+#if LWIP_IPV6 || LWIP_IPV4_SRC_ROUTING
+ , const ip_addr_t *src
+#endif /* LWIP_IPV6 || LWIP_IPV4_SRC_ROUTING */
+ );
+#if LWIP_IPV6 || LWIP_IPV4_SRC_ROUTING
+#define tcp_eff_send_mss(sendmss, src, dest) tcp_eff_send_mss_impl(sendmss, dest, src)
+#else /* LWIP_IPV6 || LWIP_IPV4_SRC_ROUTING */
+#define tcp_eff_send_mss(sendmss, src, dest) tcp_eff_send_mss_impl(sendmss, dest)
+#endif /* LWIP_IPV6 || LWIP_IPV4_SRC_ROUTING */
+#endif /* TCP_CALCULATE_EFF_SEND_MSS */
+
+#if LWIP_CALLBACK_API
+err_t tcp_recv_null(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err);
+#endif /* LWIP_CALLBACK_API */
+
+#if TCP_DEBUG || TCP_INPUT_DEBUG || TCP_OUTPUT_DEBUG
+void tcp_debug_print(struct tcp_hdr *tcphdr);
+void tcp_debug_print_flags(u8_t flags);
+void tcp_debug_print_state(enum tcp_state s);
+void tcp_debug_print_pcbs(void);
+s16_t tcp_pcbs_sane(void);
+#else
+# define tcp_debug_print(tcphdr)
+# define tcp_debug_print_flags(flags)
+# define tcp_debug_print_state(s)
+# define tcp_debug_print_pcbs()
+# define tcp_pcbs_sane() 1
+#endif /* TCP_DEBUG */
+
+/** External function (implemented in timers.c), called when TCP detects
+ * that a timer is needed (i.e. active- or time-wait-pcb found). */
+void tcp_timer_needed(void);
+
+void tcp_netif_ip_addr_changed(const ip_addr_t* old_addr, const ip_addr_t* new_addr);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_TCP */
+
+#endif /* LWIP_HDR_TCP_PRIV_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/priv/tcpip_priv.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/priv/tcpip_priv.h
new file mode 100644
index 0000000..630efb1
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/priv/tcpip_priv.h
@@ -0,0 +1,160 @@
+/**
+ * @file
+ * TCPIP API internal implementations (do not use in application code)
+ */
+
+/*
+ * 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_HDR_TCPIP_PRIV_H
+#define LWIP_HDR_TCPIP_PRIV_H
+
+#include "lwip/opt.h"
+
+#if !NO_SYS /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/tcpip.h"
+#include "lwip/sys.h"
+#include "lwip/timeouts.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct pbuf;
+struct netif;
+
+#if LWIP_MPU_COMPATIBLE
+#define API_VAR_REF(name) (*(name))
+#define API_VAR_DECLARE(type, name) type * name
+#define API_VAR_ALLOC(type, pool, name, errorval) do { \
+ name = (type *)memp_malloc(pool); \
+ if (name == NULL) { \
+ return errorval; \
+ } \
+ } while(0)
+#define API_VAR_ALLOC_POOL(type, pool, name, errorval) do { \
+ name = (type *)LWIP_MEMPOOL_ALLOC(pool); \
+ if (name == NULL) { \
+ return errorval; \
+ } \
+ } while(0)
+#define API_VAR_FREE(pool, name) memp_free(pool, name)
+#define API_VAR_FREE_POOL(pool, name) LWIP_MEMPOOL_FREE(pool, name)
+#define API_EXPR_REF(expr) (&(expr))
+#if LWIP_NETCONN_SEM_PER_THREAD
+#define API_EXPR_REF_SEM(expr) (expr)
+#else
+#define API_EXPR_REF_SEM(expr) API_EXPR_REF(expr)
+#endif
+#define API_EXPR_DEREF(expr) expr
+#define API_MSG_M_DEF(m) m
+#define API_MSG_M_DEF_C(t, m) t m
+#else /* LWIP_MPU_COMPATIBLE */
+#define API_VAR_REF(name) name
+#define API_VAR_DECLARE(type, name) type name
+#define API_VAR_ALLOC(type, pool, name, errorval)
+#define API_VAR_ALLOC_POOL(type, pool, name, errorval)
+#define API_VAR_FREE(pool, name)
+#define API_VAR_FREE_POOL(pool, name)
+#define API_EXPR_REF(expr) expr
+#define API_EXPR_REF_SEM(expr) API_EXPR_REF(expr)
+#define API_EXPR_DEREF(expr) (*(expr))
+#define API_MSG_M_DEF(m) *m
+#define API_MSG_M_DEF_C(t, m) const t * m
+#endif /* LWIP_MPU_COMPATIBLE */
+
+err_t tcpip_send_msg_wait_sem(tcpip_callback_fn fn, void *apimsg, sys_sem_t* sem);
+
+struct tcpip_api_call_data
+{
+#if !LWIP_TCPIP_CORE_LOCKING
+ err_t err;
+#if !LWIP_NETCONN_SEM_PER_THREAD
+ sys_sem_t sem;
+#endif /* LWIP_NETCONN_SEM_PER_THREAD */
+#else /* !LWIP_TCPIP_CORE_LOCKING */
+ u8_t dummy; /* avoid empty struct :-( */
+#endif /* !LWIP_TCPIP_CORE_LOCKING */
+};
+typedef err_t (*tcpip_api_call_fn)(struct tcpip_api_call_data* call);
+err_t tcpip_api_call(tcpip_api_call_fn fn, struct tcpip_api_call_data *call);
+
+enum tcpip_msg_type {
+ TCPIP_MSG_API,
+ TCPIP_MSG_API_CALL,
+ TCPIP_MSG_INPKT,
+#if LWIP_TCPIP_TIMEOUT && LWIP_TIMERS
+ TCPIP_MSG_TIMEOUT,
+ TCPIP_MSG_UNTIMEOUT,
+#endif /* LWIP_TCPIP_TIMEOUT && LWIP_TIMERS */
+ TCPIP_MSG_CALLBACK,
+ TCPIP_MSG_CALLBACK_STATIC
+};
+
+struct tcpip_msg {
+ enum tcpip_msg_type type;
+ union {
+ struct {
+ tcpip_callback_fn function;
+ void* msg;
+ } api_msg;
+ struct {
+ tcpip_api_call_fn function;
+ struct tcpip_api_call_data *arg;
+ sys_sem_t *sem;
+ } api_call;
+ struct {
+ struct pbuf *p;
+ struct netif *netif;
+ netif_input_fn input_fn;
+ } inp;
+ struct {
+ tcpip_callback_fn function;
+ void *ctx;
+ } cb;
+#if LWIP_TCPIP_TIMEOUT && LWIP_TIMERS
+ struct {
+ u32_t msecs;
+ sys_timeout_handler h;
+ void *arg;
+ } tmo;
+#endif /* LWIP_TCPIP_TIMEOUT && LWIP_TIMERS */
+ } msg;
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !NO_SYS */
+
+#endif /* LWIP_HDR_TCPIP_PRIV_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/prot/autoip.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/prot/autoip.h
new file mode 100644
index 0000000..fd3af8a
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/prot/autoip.h
@@ -0,0 +1,78 @@
+/**
+ * @file
+ * AutoIP protocol definitions
+ */
+
+/*
+ *
+ * Copyright (c) 2007 Dominik Spies <kontakt@dspies.de>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Dominik Spies <kontakt@dspies.de>
+ *
+ * This is a AutoIP implementation for the lwIP TCP/IP stack. It aims to conform
+ * with RFC 3927.
+ *
+ */
+
+#ifndef LWIP_HDR_PROT_AUTOIP_H
+#define LWIP_HDR_PROT_AUTOIP_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* 169.254.0.0 */
+#define AUTOIP_NET 0xA9FE0000
+/* 169.254.1.0 */
+#define AUTOIP_RANGE_START (AUTOIP_NET | 0x0100)
+/* 169.254.254.255 */
+#define AUTOIP_RANGE_END (AUTOIP_NET | 0xFEFF)
+
+/* RFC 3927 Constants */
+#define PROBE_WAIT 1 /* second (initial random delay) */
+#define PROBE_MIN 1 /* second (minimum delay till repeated probe) */
+#define PROBE_MAX 2 /* seconds (maximum delay till repeated probe) */
+#define PROBE_NUM 3 /* (number of probe packets) */
+#define ANNOUNCE_NUM 2 /* (number of announcement packets) */
+#define ANNOUNCE_INTERVAL 2 /* seconds (time between announcement packets) */
+#define ANNOUNCE_WAIT 2 /* seconds (delay before announcing) */
+#define MAX_CONFLICTS 10 /* (max conflicts before rate limiting) */
+#define RATE_LIMIT_INTERVAL 60 /* seconds (delay between successive attempts) */
+#define DEFEND_INTERVAL 10 /* seconds (min. wait between defensive ARPs) */
+
+/* AutoIP client states */
+typedef enum {
+ AUTOIP_STATE_OFF = 0,
+ AUTOIP_STATE_PROBING = 1,
+ AUTOIP_STATE_ANNOUNCING = 2,
+ AUTOIP_STATE_BOUND = 3
+} autoip_state_enum_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_PROT_AUTOIP_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/prot/dhcp.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/prot/dhcp.h
new file mode 100644
index 0000000..112953c
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/prot/dhcp.h
@@ -0,0 +1,183 @@
+/**
+ * @file
+ * DHCP protocol definitions
+ */
+
+/*
+ * Copyright (c) 2001-2004 Leon Woestenberg <leon.woestenberg@gmx.net>
+ * Copyright (c) 2001-2004 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Leon Woestenberg <leon.woestenberg@gmx.net>
+ *
+ */
+#ifndef LWIP_HDR_PROT_DHCP_H
+#define LWIP_HDR_PROT_DHCP_H
+
+#include "lwip/opt.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define DHCP_CLIENT_PORT 68
+#define DHCP_SERVER_PORT 67
+
+
+ /* DHCP message item offsets and length */
+#define DHCP_CHADDR_LEN 16U
+#define DHCP_SNAME_OFS 44U
+#define DHCP_SNAME_LEN 64U
+#define DHCP_FILE_OFS 108U
+#define DHCP_FILE_LEN 128U
+#define DHCP_MSG_LEN 236U
+#define DHCP_OPTIONS_OFS (DHCP_MSG_LEN + 4U) /* 4 byte: cookie */
+
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+/** minimum set of fields of any DHCP message */
+struct dhcp_msg
+{
+ PACK_STRUCT_FLD_8(u8_t op);
+ PACK_STRUCT_FLD_8(u8_t htype);
+ PACK_STRUCT_FLD_8(u8_t hlen);
+ PACK_STRUCT_FLD_8(u8_t hops);
+ PACK_STRUCT_FIELD(u32_t xid);
+ PACK_STRUCT_FIELD(u16_t secs);
+ PACK_STRUCT_FIELD(u16_t flags);
+ PACK_STRUCT_FLD_S(ip4_addr_p_t ciaddr);
+ PACK_STRUCT_FLD_S(ip4_addr_p_t yiaddr);
+ PACK_STRUCT_FLD_S(ip4_addr_p_t siaddr);
+ PACK_STRUCT_FLD_S(ip4_addr_p_t giaddr);
+ PACK_STRUCT_FLD_8(u8_t chaddr[DHCP_CHADDR_LEN]);
+ PACK_STRUCT_FLD_8(u8_t sname[DHCP_SNAME_LEN]);
+ PACK_STRUCT_FLD_8(u8_t file[DHCP_FILE_LEN]);
+ PACK_STRUCT_FIELD(u32_t cookie);
+#define DHCP_MIN_OPTIONS_LEN 68U
+/** make sure user does not configure this too small */
+#if ((defined(DHCP_OPTIONS_LEN)) && (DHCP_OPTIONS_LEN < DHCP_MIN_OPTIONS_LEN))
+# undef DHCP_OPTIONS_LEN
+#endif
+/** allow this to be configured in lwipopts.h, but not too small */
+#if (!defined(DHCP_OPTIONS_LEN))
+/** set this to be sufficient for your options in outgoing DHCP msgs */
+# define DHCP_OPTIONS_LEN DHCP_MIN_OPTIONS_LEN
+#endif
+ PACK_STRUCT_FLD_8(u8_t options[DHCP_OPTIONS_LEN]);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+
+/* DHCP client states */
+typedef enum {
+ DHCP_STATE_OFF = 0,
+ DHCP_STATE_REQUESTING = 1,
+ DHCP_STATE_INIT = 2,
+ DHCP_STATE_REBOOTING = 3,
+ DHCP_STATE_REBINDING = 4,
+ DHCP_STATE_RENEWING = 5,
+ DHCP_STATE_SELECTING = 6,
+ DHCP_STATE_INFORMING = 7,
+ DHCP_STATE_CHECKING = 8,
+ DHCP_STATE_PERMANENT = 9, /* not yet implemented */
+ DHCP_STATE_BOUND = 10,
+ DHCP_STATE_RELEASING = 11, /* not yet implemented */
+ DHCP_STATE_BACKING_OFF = 12
+} dhcp_state_enum_t;
+
+/* DHCP op codes */
+#define DHCP_BOOTREQUEST 1
+#define DHCP_BOOTREPLY 2
+
+/* DHCP message types */
+#define DHCP_DISCOVER 1
+#define DHCP_OFFER 2
+#define DHCP_REQUEST 3
+#define DHCP_DECLINE 4
+#define DHCP_ACK 5
+#define DHCP_NAK 6
+#define DHCP_RELEASE 7
+#define DHCP_INFORM 8
+
+/** DHCP hardware type, currently only ethernet is supported */
+#define DHCP_HTYPE_ETH 1
+
+#define DHCP_MAGIC_COOKIE 0x63825363UL
+
+/* This is a list of options for BOOTP and DHCP, see RFC 2132 for descriptions */
+
+/* BootP options */
+#define DHCP_OPTION_PAD 0
+#define DHCP_OPTION_SUBNET_MASK 1 /* RFC 2132 3.3 */
+#define DHCP_OPTION_ROUTER 3
+#define DHCP_OPTION_DNS_SERVER 6
+#define DHCP_OPTION_HOSTNAME 12
+#define DHCP_OPTION_IP_TTL 23
+#define DHCP_OPTION_MTU 26
+#define DHCP_OPTION_BROADCAST 28
+#define DHCP_OPTION_TCP_TTL 37
+#define DHCP_OPTION_NTP 42
+#define DHCP_OPTION_END 255
+
+/* DHCP options */
+#define DHCP_OPTION_REQUESTED_IP 50 /* RFC 2132 9.1, requested IP address */
+#define DHCP_OPTION_LEASE_TIME 51 /* RFC 2132 9.2, time in seconds, in 4 bytes */
+#define DHCP_OPTION_OVERLOAD 52 /* RFC2132 9.3, use file and/or sname field for options */
+
+#define DHCP_OPTION_MESSAGE_TYPE 53 /* RFC 2132 9.6, important for DHCP */
+#define DHCP_OPTION_MESSAGE_TYPE_LEN 1
+
+#define DHCP_OPTION_SERVER_ID 54 /* RFC 2132 9.7, server IP address */
+#define DHCP_OPTION_PARAMETER_REQUEST_LIST 55 /* RFC 2132 9.8, requested option types */
+
+#define DHCP_OPTION_MAX_MSG_SIZE 57 /* RFC 2132 9.10, message size accepted >= 576 */
+#define DHCP_OPTION_MAX_MSG_SIZE_LEN 2
+
+#define DHCP_OPTION_T1 58 /* T1 renewal time */
+#define DHCP_OPTION_T2 59 /* T2 rebinding time */
+#define DHCP_OPTION_US 60
+#define DHCP_OPTION_CLIENT_ID 61
+#define DHCP_OPTION_TFTP_SERVERNAME 66
+#define DHCP_OPTION_BOOTFILE 67
+
+/* possible combinations of overloading the file and sname fields with options */
+#define DHCP_OVERLOAD_NONE 0
+#define DHCP_OVERLOAD_FILE 1
+#define DHCP_OVERLOAD_SNAME 2
+#define DHCP_OVERLOAD_SNAME_FILE 3
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /*LWIP_HDR_PROT_DHCP_H*/
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/prot/dns.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/prot/dns.h
new file mode 100644
index 0000000..94782d6
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/prot/dns.h
@@ -0,0 +1,140 @@
+/**
+ * @file
+ * DNS - host name to IP address resolver.
+ */
+
+/*
+ * Port to lwIP from uIP
+ * by Jim Pettinato April 2007
+ *
+ * security fixes and more by Simon Goldschmidt
+ *
+ * uIP version Copyright (c) 2002-2003, Adam Dunkels.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef LWIP_HDR_PROT_DNS_H
+#define LWIP_HDR_PROT_DNS_H
+
+#include "lwip/arch.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** DNS server port address */
+#ifndef DNS_SERVER_PORT
+#define DNS_SERVER_PORT 53
+#endif
+
+/* DNS field TYPE used for "Resource Records" */
+#define DNS_RRTYPE_A 1 /* a host address */
+#define DNS_RRTYPE_NS 2 /* an authoritative name server */
+#define DNS_RRTYPE_MD 3 /* a mail destination (Obsolete - use MX) */
+#define DNS_RRTYPE_MF 4 /* a mail forwarder (Obsolete - use MX) */
+#define DNS_RRTYPE_CNAME 5 /* the canonical name for an alias */
+#define DNS_RRTYPE_SOA 6 /* marks the start of a zone of authority */
+#define DNS_RRTYPE_MB 7 /* a mailbox domain name (EXPERIMENTAL) */
+#define DNS_RRTYPE_MG 8 /* a mail group member (EXPERIMENTAL) */
+#define DNS_RRTYPE_MR 9 /* a mail rename domain name (EXPERIMENTAL) */
+#define DNS_RRTYPE_NULL 10 /* a null RR (EXPERIMENTAL) */
+#define DNS_RRTYPE_WKS 11 /* a well known service description */
+#define DNS_RRTYPE_PTR 12 /* a domain name pointer */
+#define DNS_RRTYPE_HINFO 13 /* host information */
+#define DNS_RRTYPE_MINFO 14 /* mailbox or mail list information */
+#define DNS_RRTYPE_MX 15 /* mail exchange */
+#define DNS_RRTYPE_TXT 16 /* text strings */
+#define DNS_RRTYPE_AAAA 28 /* IPv6 address */
+#define DNS_RRTYPE_SRV 33 /* service location */
+#define DNS_RRTYPE_ANY 255 /* any type */
+
+/* DNS field CLASS used for "Resource Records" */
+#define DNS_RRCLASS_IN 1 /* the Internet */
+#define DNS_RRCLASS_CS 2 /* the CSNET class (Obsolete - used only for examples in some obsolete RFCs) */
+#define DNS_RRCLASS_CH 3 /* the CHAOS class */
+#define DNS_RRCLASS_HS 4 /* Hesiod [Dyer 87] */
+#define DNS_RRCLASS_ANY 255 /* any class */
+#define DNS_RRCLASS_FLUSH 0x800 /* Flush bit */
+
+/* DNS protocol flags */
+#define DNS_FLAG1_RESPONSE 0x80
+#define DNS_FLAG1_OPCODE_STATUS 0x10
+#define DNS_FLAG1_OPCODE_INVERSE 0x08
+#define DNS_FLAG1_OPCODE_STANDARD 0x00
+#define DNS_FLAG1_AUTHORATIVE 0x04
+#define DNS_FLAG1_TRUNC 0x02
+#define DNS_FLAG1_RD 0x01
+#define DNS_FLAG2_RA 0x80
+#define DNS_FLAG2_ERR_MASK 0x0f
+#define DNS_FLAG2_ERR_NONE 0x00
+#define DNS_FLAG2_ERR_NAME 0x03
+
+#define DNS_HDR_GET_OPCODE(hdr) ((((hdr)->flags1) >> 3) & 0xF)
+
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+/** DNS message header */
+struct dns_hdr {
+ PACK_STRUCT_FIELD(u16_t id);
+ PACK_STRUCT_FLD_8(u8_t flags1);
+ PACK_STRUCT_FLD_8(u8_t flags2);
+ PACK_STRUCT_FIELD(u16_t numquestions);
+ PACK_STRUCT_FIELD(u16_t numanswers);
+ PACK_STRUCT_FIELD(u16_t numauthrr);
+ PACK_STRUCT_FIELD(u16_t numextrarr);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+#define SIZEOF_DNS_HDR 12
+
+
+/* Multicast DNS definitions */
+
+/** UDP port for multicast DNS queries */
+#ifndef DNS_MQUERY_PORT
+#define DNS_MQUERY_PORT 5353
+#endif
+
+/* IPv4 group for multicast DNS queries: 224.0.0.251 */
+#ifndef DNS_MQUERY_IPV4_GROUP_INIT
+#define DNS_MQUERY_IPV4_GROUP_INIT IPADDR4_INIT_BYTES(224,0,0,251)
+#endif
+
+/* IPv6 group for multicast DNS queries: FF02::FB */
+#ifndef DNS_MQUERY_IPV6_GROUP_INIT
+#define DNS_MQUERY_IPV6_GROUP_INIT IPADDR6_INIT_HOST(0xFF020000,0,0,0xFB)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_PROT_DNS_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/prot/etharp.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/prot/etharp.h
new file mode 100644
index 0000000..ec78305
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/prot/etharp.h
@@ -0,0 +1,91 @@
+/**
+ * @file
+ * ARP protocol definitions
+ */
+
+/*
+ * 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_HDR_PROT_ETHARP_H
+#define LWIP_HDR_PROT_ETHARP_H
+
+#include "lwip/arch.h"
+#include "lwip/prot/ethernet.h"
+#include "lwip/ip4_addr.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef ETHARP_HWADDR_LEN
+#define ETHARP_HWADDR_LEN ETH_HWADDR_LEN
+#endif
+
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+/** the ARP message, see RFC 826 ("Packet format") */
+struct etharp_hdr {
+ PACK_STRUCT_FIELD(u16_t hwtype);
+ PACK_STRUCT_FIELD(u16_t proto);
+ PACK_STRUCT_FLD_8(u8_t hwlen);
+ PACK_STRUCT_FLD_8(u8_t protolen);
+ PACK_STRUCT_FIELD(u16_t opcode);
+ PACK_STRUCT_FLD_S(struct eth_addr shwaddr);
+ PACK_STRUCT_FLD_S(struct ip4_addr2 sipaddr);
+ PACK_STRUCT_FLD_S(struct eth_addr dhwaddr);
+ PACK_STRUCT_FLD_S(struct ip4_addr2 dipaddr);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+#define SIZEOF_ETHARP_HDR 28
+
+/* ARP hwtype values */
+enum etharp_hwtype {
+ HWTYPE_ETHERNET = 1
+ /* others not used */
+};
+
+/* ARP message types (opcodes) */
+enum etharp_opcode {
+ ARP_REQUEST = 1,
+ ARP_REPLY = 2
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_PROT_ETHARP_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/prot/ethernet.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/prot/ethernet.h
new file mode 100644
index 0000000..e4baa29
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/prot/ethernet.h
@@ -0,0 +1,170 @@
+/**
+ * @file
+ * Ethernet protocol definitions
+ */
+
+/*
+ * 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_HDR_PROT_ETHERNET_H
+#define LWIP_HDR_PROT_ETHERNET_H
+
+#include "lwip/arch.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef ETH_HWADDR_LEN
+#ifdef ETHARP_HWADDR_LEN
+#define ETH_HWADDR_LEN ETHARP_HWADDR_LEN /* compatibility mode */
+#else
+#define ETH_HWADDR_LEN 6
+#endif
+#endif
+
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct eth_addr {
+ PACK_STRUCT_FLD_8(u8_t addr[ETH_HWADDR_LEN]);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+/** Ethernet header */
+struct eth_hdr {
+#if ETH_PAD_SIZE
+ PACK_STRUCT_FLD_8(u8_t padding[ETH_PAD_SIZE]);
+#endif
+ PACK_STRUCT_FLD_S(struct eth_addr dest);
+ PACK_STRUCT_FLD_S(struct eth_addr src);
+ PACK_STRUCT_FIELD(u16_t type);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+#define SIZEOF_ETH_HDR (14 + ETH_PAD_SIZE)
+
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+/** VLAN header inserted between ethernet header and payload
+ * if 'type' in ethernet header is ETHTYPE_VLAN.
+ * See IEEE802.Q */
+struct eth_vlan_hdr {
+ PACK_STRUCT_FIELD(u16_t prio_vid);
+ PACK_STRUCT_FIELD(u16_t tpid);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+#define SIZEOF_VLAN_HDR 4
+#define VLAN_ID(vlan_hdr) (lwip_htons((vlan_hdr)->prio_vid) & 0xFFF)
+
+/**
+ * @ingroup ethernet
+ * A list of often ethtypes (although lwIP does not use all of them): */
+enum eth_type {
+ /** Internet protocol v4 */
+ ETHTYPE_IP = 0x0800U,
+ /** Address resolution protocol */
+ ETHTYPE_ARP = 0x0806U,
+ /** Wake on lan */
+ ETHTYPE_WOL = 0x0842U,
+ /** RARP */
+ ETHTYPE_RARP = 0x8035U,
+ /** Virtual local area network */
+ ETHTYPE_VLAN = 0x8100U,
+ /** Internet protocol v6 */
+ ETHTYPE_IPV6 = 0x86DDU,
+ /** PPP Over Ethernet Discovery Stage */
+ ETHTYPE_PPPOEDISC = 0x8863U,
+ /** PPP Over Ethernet Session Stage */
+ ETHTYPE_PPPOE = 0x8864U,
+ /** Jumbo Frames */
+ ETHTYPE_JUMBO = 0x8870U,
+ /** Process field network */
+ ETHTYPE_PROFINET = 0x8892U,
+ /** Ethernet for control automation technology */
+ ETHTYPE_ETHERCAT = 0x88A4U,
+ /** Link layer discovery protocol */
+ ETHTYPE_LLDP = 0x88CCU,
+ /** Serial real-time communication system */
+ ETHTYPE_SERCOS = 0x88CDU,
+ /** Media redundancy protocol */
+ ETHTYPE_MRP = 0x88E3U,
+ /** Precision time protocol */
+ ETHTYPE_PTP = 0x88F7U,
+ /** Q-in-Q, 802.1ad */
+ ETHTYPE_QINQ = 0x9100U
+};
+
+/** The 24-bit IANA IPv4-multicast OUI is 01-00-5e: */
+#define LL_IP4_MULTICAST_ADDR_0 0x01
+#define LL_IP4_MULTICAST_ADDR_1 0x00
+#define LL_IP4_MULTICAST_ADDR_2 0x5e
+
+/** IPv6 multicast uses this prefix */
+#define LL_IP6_MULTICAST_ADDR_0 0x33
+#define LL_IP6_MULTICAST_ADDR_1 0x33
+
+/** MEMCPY-like macro to copy to/from struct eth_addr's that are local variables
+ * or known to be 32-bit aligned within the protocol header. */
+#ifndef ETHADDR32_COPY
+#define ETHADDR32_COPY(dst, src) SMEMCPY(dst, src, ETH_HWADDR_LEN)
+#endif
+
+/** MEMCPY-like macro to copy to/from struct eth_addr's that are no local
+ * variables and known to be 16-bit aligned within the protocol header. */
+#ifndef ETHADDR16_COPY
+#define ETHADDR16_COPY(dst, src) SMEMCPY(dst, src, ETH_HWADDR_LEN)
+#endif
+
+#define eth_addr_cmp(addr1, addr2) (memcmp((addr1)->addr, (addr2)->addr, ETH_HWADDR_LEN) == 0)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_PROT_ETHERNET_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/prot/icmp.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/prot/icmp.h
new file mode 100644
index 0000000..7d19385
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/prot/icmp.h
@@ -0,0 +1,91 @@
+/**
+ * @file
+ * ICMP protocol definitions
+ */
+
+/*
+ * 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_HDR_PROT_ICMP_H
+#define LWIP_HDR_PROT_ICMP_H
+
+#include "lwip/arch.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ICMP_ER 0 /* echo reply */
+#define ICMP_DUR 3 /* destination unreachable */
+#define ICMP_SQ 4 /* source quench */
+#define ICMP_RD 5 /* redirect */
+#define ICMP_ECHO 8 /* echo */
+#define ICMP_TE 11 /* time exceeded */
+#define ICMP_PP 12 /* parameter problem */
+#define ICMP_TS 13 /* timestamp */
+#define ICMP_TSR 14 /* timestamp reply */
+#define ICMP_IRQ 15 /* information request */
+#define ICMP_IR 16 /* information reply */
+#define ICMP_AM 17 /* address mask request */
+#define ICMP_AMR 18 /* address mask reply */
+
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+/** This is the standard ICMP header only that the u32_t data
+ * is split to two u16_t like ICMP echo needs it.
+ * This header is also used for other ICMP types that do not
+ * use the data part.
+ */
+PACK_STRUCT_BEGIN
+struct icmp_echo_hdr {
+ PACK_STRUCT_FLD_8(u8_t type);
+ PACK_STRUCT_FLD_8(u8_t code);
+ PACK_STRUCT_FIELD(u16_t chksum);
+ PACK_STRUCT_FIELD(u16_t id);
+ PACK_STRUCT_FIELD(u16_t seqno);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+/* Compatibility defines, old versions used to combine type and code to an u16_t */
+#define ICMPH_TYPE(hdr) ((hdr)->type)
+#define ICMPH_CODE(hdr) ((hdr)->code)
+#define ICMPH_TYPE_SET(hdr, t) ((hdr)->type = (t))
+#define ICMPH_CODE_SET(hdr, c) ((hdr)->code = (c))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_PROT_ICMP_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/prot/icmp6.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/prot/icmp6.h
new file mode 100644
index 0000000..3461120
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/prot/icmp6.h
@@ -0,0 +1,170 @@
+/**
+ * @file
+ * ICMP6 protocol definitions
+ */
+
+/*
+ * 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_HDR_PROT_ICMP6_H
+#define LWIP_HDR_PROT_ICMP6_H
+
+#include "lwip/arch.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** ICMP type */
+enum icmp6_type {
+ /** Destination unreachable */
+ ICMP6_TYPE_DUR = 1,
+ /** Packet too big */
+ ICMP6_TYPE_PTB = 2,
+ /** Time exceeded */
+ ICMP6_TYPE_TE = 3,
+ /** Parameter problem */
+ ICMP6_TYPE_PP = 4,
+ /** Private experimentation */
+ ICMP6_TYPE_PE1 = 100,
+ /** Private experimentation */
+ ICMP6_TYPE_PE2 = 101,
+ /** Reserved for expansion of error messages */
+ ICMP6_TYPE_RSV_ERR = 127,
+
+ /** Echo request */
+ ICMP6_TYPE_EREQ = 128,
+ /** Echo reply */
+ ICMP6_TYPE_EREP = 129,
+ /** Multicast listener query */
+ ICMP6_TYPE_MLQ = 130,
+ /** Multicast listener report */
+ ICMP6_TYPE_MLR = 131,
+ /** Multicast listener done */
+ ICMP6_TYPE_MLD = 132,
+ /** Router solicitation */
+ ICMP6_TYPE_RS = 133,
+ /** Router advertisement */
+ ICMP6_TYPE_RA = 134,
+ /** Neighbor solicitation */
+ ICMP6_TYPE_NS = 135,
+ /** Neighbor advertisement */
+ ICMP6_TYPE_NA = 136,
+ /** Redirect */
+ ICMP6_TYPE_RD = 137,
+ /** Multicast router advertisement */
+ ICMP6_TYPE_MRA = 151,
+ /** Multicast router solicitation */
+ ICMP6_TYPE_MRS = 152,
+ /** Multicast router termination */
+ ICMP6_TYPE_MRT = 153,
+ /** Private experimentation */
+ ICMP6_TYPE_PE3 = 200,
+ /** Private experimentation */
+ ICMP6_TYPE_PE4 = 201,
+ /** Reserved for expansion of informational messages */
+ ICMP6_TYPE_RSV_INF = 255
+};
+
+/** ICMP destination unreachable codes */
+enum icmp6_dur_code {
+ /** No route to destination */
+ ICMP6_DUR_NO_ROUTE = 0,
+ /** Communication with destination administratively prohibited */
+ ICMP6_DUR_PROHIBITED = 1,
+ /** Beyond scope of source address */
+ ICMP6_DUR_SCOPE = 2,
+ /** Address unreachable */
+ ICMP6_DUR_ADDRESS = 3,
+ /** Port unreachable */
+ ICMP6_DUR_PORT = 4,
+ /** Source address failed ingress/egress policy */
+ ICMP6_DUR_POLICY = 5,
+ /** Reject route to destination */
+ ICMP6_DUR_REJECT_ROUTE = 6
+};
+
+/** ICMP time exceeded codes */
+enum icmp6_te_code {
+ /** Hop limit exceeded in transit */
+ ICMP6_TE_HL = 0,
+ /** Fragment reassembly time exceeded */
+ ICMP6_TE_FRAG = 1
+};
+
+/** ICMP parameter code */
+enum icmp6_pp_code {
+ /** Erroneous header field encountered */
+ ICMP6_PP_FIELD = 0,
+ /** Unrecognized next header type encountered */
+ ICMP6_PP_HEADER = 1,
+ /** Unrecognized IPv6 option encountered */
+ ICMP6_PP_OPTION = 2
+};
+
+/** This is the standard ICMP6 header. */
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct icmp6_hdr {
+ PACK_STRUCT_FLD_8(u8_t type);
+ PACK_STRUCT_FLD_8(u8_t code);
+ PACK_STRUCT_FIELD(u16_t chksum);
+ PACK_STRUCT_FIELD(u32_t data);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+/** This is the ICMP6 header adapted for echo req/resp. */
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct icmp6_echo_hdr {
+ PACK_STRUCT_FLD_8(u8_t type);
+ PACK_STRUCT_FLD_8(u8_t code);
+ PACK_STRUCT_FIELD(u16_t chksum);
+ PACK_STRUCT_FIELD(u16_t id);
+ PACK_STRUCT_FIELD(u16_t seqno);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_PROT_ICMP6_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/prot/igmp.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/prot/igmp.h
new file mode 100644
index 0000000..d60cb31
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/prot/igmp.h
@@ -0,0 +1,90 @@
+/**
+ * @file
+ * IGMP protocol definitions
+ */
+
+/*
+ * 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_HDR_PROT_IGMP_H
+#define LWIP_HDR_PROT_IGMP_H
+
+#include "lwip/arch.h"
+#include "lwip/ip4_addr.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * IGMP constants
+ */
+#define IGMP_TTL 1
+#define IGMP_MINLEN 8
+#define ROUTER_ALERT 0x9404U
+#define ROUTER_ALERTLEN 4
+
+/*
+ * IGMP message types, including version number.
+ */
+#define IGMP_MEMB_QUERY 0x11 /* Membership query */
+#define IGMP_V1_MEMB_REPORT 0x12 /* Ver. 1 membership report */
+#define IGMP_V2_MEMB_REPORT 0x16 /* Ver. 2 membership report */
+#define IGMP_LEAVE_GROUP 0x17 /* Leave-group message */
+
+/* Group membership states */
+#define IGMP_GROUP_NON_MEMBER 0
+#define IGMP_GROUP_DELAYING_MEMBER 1
+#define IGMP_GROUP_IDLE_MEMBER 2
+
+/**
+ * IGMP packet format.
+ */
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct igmp_msg {
+ PACK_STRUCT_FLD_8(u8_t igmp_msgtype);
+ PACK_STRUCT_FLD_8(u8_t igmp_maxresp);
+ PACK_STRUCT_FIELD(u16_t igmp_checksum);
+ PACK_STRUCT_FLD_S(ip4_addr_p_t igmp_group_address);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_PROT_IGMP_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/prot/ip.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/prot/ip.h
new file mode 100644
index 0000000..bbfae36
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/prot/ip.h
@@ -0,0 +1,51 @@
+/**
+ * @file
+ * IP protocol definitions
+ */
+
+/*
+ * 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_HDR_PROT_IP_H
+#define LWIP_HDR_PROT_IP_H
+
+#include "lwip/arch.h"
+
+#define IP_PROTO_ICMP 1
+#define IP_PROTO_IGMP 2
+#define IP_PROTO_UDP 17
+#define IP_PROTO_UDPLITE 136
+#define IP_PROTO_TCP 6
+
+/** This operates on a void* by loading the first byte */
+#define IP_HDR_GET_VERSION(ptr) ((*(u8_t*)(ptr)) >> 4)
+
+#endif /* LWIP_HDR_PROT_IP_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/prot/ip4.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/prot/ip4.h
new file mode 100644
index 0000000..bd442c6
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/prot/ip4.h
@@ -0,0 +1,127 @@
+/**
+ * @file
+ * IPv4 protocol definitions
+ */
+
+/*
+ * 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_HDR_PROT_IP4_H
+#define LWIP_HDR_PROT_IP4_H
+
+#include "lwip/arch.h"
+#include "lwip/ip4_addr.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** This is the packed version of ip4_addr_t,
+ used in network headers that are itself packed */
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct ip4_addr_packed {
+ PACK_STRUCT_FIELD(u32_t addr);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+typedef struct ip4_addr_packed ip4_addr_p_t;
+
+/* Size of the IPv4 header. Same as 'sizeof(struct ip_hdr)'. */
+#define IP_HLEN 20
+
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+/* The IPv4 header */
+struct ip_hdr {
+ /* version / header length */
+ PACK_STRUCT_FLD_8(u8_t _v_hl);
+ /* type of service */
+ PACK_STRUCT_FLD_8(u8_t _tos);
+ /* total length */
+ PACK_STRUCT_FIELD(u16_t _len);
+ /* identification */
+ PACK_STRUCT_FIELD(u16_t _id);
+ /* fragment offset field */
+ PACK_STRUCT_FIELD(u16_t _offset);
+#define IP_RF 0x8000U /* reserved fragment flag */
+#define IP_DF 0x4000U /* don't fragment flag */
+#define IP_MF 0x2000U /* more fragments flag */
+#define IP_OFFMASK 0x1fffU /* mask for fragmenting bits */
+ /* time to live */
+ PACK_STRUCT_FLD_8(u8_t _ttl);
+ /* protocol*/
+ PACK_STRUCT_FLD_8(u8_t _proto);
+ /* checksum */
+ PACK_STRUCT_FIELD(u16_t _chksum);
+ /* source and destination IP addresses */
+ PACK_STRUCT_FLD_S(ip4_addr_p_t src);
+ PACK_STRUCT_FLD_S(ip4_addr_p_t dest);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+/* Macros to get struct ip_hdr fields: */
+#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)
+#define IPH_TTL(hdr) ((hdr)->_ttl)
+#define IPH_PROTO(hdr) ((hdr)->_proto)
+#define IPH_CHKSUM(hdr) ((hdr)->_chksum)
+
+/* Macros to set struct ip_hdr fields: */
+#define IPH_VHL_SET(hdr, v, hl) (hdr)->_v_hl = (u8_t)((((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)
+#define IPH_TTL_SET(hdr, ttl) (hdr)->_ttl = (u8_t)(ttl)
+#define IPH_PROTO_SET(hdr, proto) (hdr)->_proto = (u8_t)(proto)
+#define IPH_CHKSUM_SET(hdr, chksum) (hdr)->_chksum = (chksum)
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_PROT_IP4_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/prot/ip6.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/prot/ip6.h
new file mode 100644
index 0000000..6e1e263
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/prot/ip6.h
@@ -0,0 +1,169 @@
+/**
+ * @file
+ * IPv6 protocol definitions
+ */
+
+/*
+ * 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_HDR_PROT_IP6_H
+#define LWIP_HDR_PROT_IP6_H
+
+#include "lwip/arch.h"
+#include "lwip/ip6_addr.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** This is the packed version of ip6_addr_t,
+ used in network headers that are itself packed */
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct ip6_addr_packed {
+ PACK_STRUCT_FIELD(u32_t addr[4]);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+typedef struct ip6_addr_packed ip6_addr_p_t;
+
+#define IP6_HLEN 40
+
+#define IP6_NEXTH_HOPBYHOP 0
+#define IP6_NEXTH_TCP 6
+#define IP6_NEXTH_UDP 17
+#define IP6_NEXTH_ENCAPS 41
+#define IP6_NEXTH_ROUTING 43
+#define IP6_NEXTH_FRAGMENT 44
+#define IP6_NEXTH_ICMP6 58
+#define IP6_NEXTH_NONE 59
+#define IP6_NEXTH_DESTOPTS 60
+#define IP6_NEXTH_UDPLITE 136
+
+/** The IPv6 header. */
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct ip6_hdr {
+ /** version / traffic class / flow label */
+ PACK_STRUCT_FIELD(u32_t _v_tc_fl);
+ /** payload length */
+ PACK_STRUCT_FIELD(u16_t _plen);
+ /** next header */
+ PACK_STRUCT_FLD_8(u8_t _nexth);
+ /** hop limit */
+ PACK_STRUCT_FLD_8(u8_t _hoplim);
+ /** source and destination IP addresses */
+ PACK_STRUCT_FLD_S(ip6_addr_p_t src);
+ PACK_STRUCT_FLD_S(ip6_addr_p_t dest);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+/* Hop-by-hop router alert option. */
+#define IP6_HBH_HLEN 8
+#define IP6_PAD1_OPTION 0
+#define IP6_PADN_ALERT_OPTION 1
+#define IP6_ROUTER_ALERT_OPTION 5
+#define IP6_ROUTER_ALERT_VALUE_MLD 0
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct ip6_hbh_hdr {
+ /* next header */
+ PACK_STRUCT_FLD_8(u8_t _nexth);
+ /* header length */
+ PACK_STRUCT_FLD_8(u8_t _hlen);
+ /* router alert option type */
+ PACK_STRUCT_FLD_8(u8_t _ra_opt_type);
+ /* router alert option data len */
+ PACK_STRUCT_FLD_8(u8_t _ra_opt_dlen);
+ /* router alert option data */
+ PACK_STRUCT_FIELD(u16_t _ra_opt_data);
+ /* PadN option type */
+ PACK_STRUCT_FLD_8(u8_t _padn_opt_type);
+ /* PadN option data len */
+ PACK_STRUCT_FLD_8(u8_t _padn_opt_dlen);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+/* Fragment header. */
+#define IP6_FRAG_HLEN 8
+#define IP6_FRAG_OFFSET_MASK 0xfff8
+#define IP6_FRAG_MORE_FLAG 0x0001
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct ip6_frag_hdr {
+ /* next header */
+ PACK_STRUCT_FLD_8(u8_t _nexth);
+ /* reserved */
+ PACK_STRUCT_FLD_8(u8_t reserved);
+ /* fragment offset */
+ PACK_STRUCT_FIELD(u16_t _fragment_offset);
+ /* fragmented packet identification */
+ PACK_STRUCT_FIELD(u32_t _identification);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+#define IP6H_V(hdr) ((lwip_ntohl((hdr)->_v_tc_fl) >> 28) & 0x0f)
+#define IP6H_TC(hdr) ((lwip_ntohl((hdr)->_v_tc_fl) >> 20) & 0xff)
+#define IP6H_FL(hdr) (lwip_ntohl((hdr)->_v_tc_fl) & 0x000fffff)
+#define IP6H_PLEN(hdr) (lwip_ntohs((hdr)->_plen))
+#define IP6H_NEXTH(hdr) ((hdr)->_nexth)
+#define IP6H_NEXTH_P(hdr) ((u8_t *)(hdr) + 6)
+#define IP6H_HOPLIM(hdr) ((hdr)->_hoplim)
+
+#define IP6H_VTCFL_SET(hdr, v, tc, fl) (hdr)->_v_tc_fl = (lwip_htonl((((u32_t)(v)) << 28) | (((u32_t)(tc)) << 20) | (fl)))
+#define IP6H_PLEN_SET(hdr, plen) (hdr)->_plen = lwip_htons(plen)
+#define IP6H_NEXTH_SET(hdr, nexth) (hdr)->_nexth = (nexth)
+#define IP6H_HOPLIM_SET(hdr, hl) (hdr)->_hoplim = (u8_t)(hl)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_PROT_IP6_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/prot/mld6.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/prot/mld6.h
new file mode 100644
index 0000000..be3a006
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/prot/mld6.h
@@ -0,0 +1,70 @@
+/**
+ * @file
+ * MLD6 protocol definitions
+ */
+
+/*
+ * 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_HDR_PROT_MLD6_H
+#define LWIP_HDR_PROT_MLD6_H
+
+#include "lwip/arch.h"
+#include "lwip/prot/ip6.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Multicast listener report/query/done message header. */
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct mld_header {
+ PACK_STRUCT_FLD_8(u8_t type);
+ PACK_STRUCT_FLD_8(u8_t code);
+ PACK_STRUCT_FIELD(u16_t chksum);
+ PACK_STRUCT_FIELD(u16_t max_resp_delay);
+ PACK_STRUCT_FIELD(u16_t reserved);
+ PACK_STRUCT_FLD_S(ip6_addr_p_t multicast_address);
+ /* Options follow. */
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_PROT_MLD6_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/prot/nd6.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/prot/nd6.h
new file mode 100644
index 0000000..2d4903d
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/prot/nd6.h
@@ -0,0 +1,277 @@
+/**
+ * @file
+ * ND6 protocol definitions
+ */
+
+/*
+ * 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_HDR_PROT_ND6_H
+#define LWIP_HDR_PROT_ND6_H
+
+#include "lwip/arch.h"
+#include "lwip/ip6_addr.h"
+#include "lwip/prot/ip6.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Neighbor solicitation message header. */
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct ns_header {
+ PACK_STRUCT_FLD_8(u8_t type);
+ PACK_STRUCT_FLD_8(u8_t code);
+ PACK_STRUCT_FIELD(u16_t chksum);
+ PACK_STRUCT_FIELD(u32_t reserved);
+ PACK_STRUCT_FLD_S(ip6_addr_p_t target_address);
+ /* Options follow. */
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+/** Neighbor advertisement message header. */
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct na_header {
+ PACK_STRUCT_FLD_8(u8_t type);
+ PACK_STRUCT_FLD_8(u8_t code);
+ PACK_STRUCT_FIELD(u16_t chksum);
+ PACK_STRUCT_FLD_8(u8_t flags);
+ PACK_STRUCT_FLD_8(u8_t reserved[3]);
+ PACK_STRUCT_FLD_S(ip6_addr_p_t target_address);
+ /* Options follow. */
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+#define ND6_FLAG_ROUTER (0x80)
+#define ND6_FLAG_SOLICITED (0x40)
+#define ND6_FLAG_OVERRIDE (0x20)
+
+/** Router solicitation message header. */
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct rs_header {
+ PACK_STRUCT_FLD_8(u8_t type);
+ PACK_STRUCT_FLD_8(u8_t code);
+ PACK_STRUCT_FIELD(u16_t chksum);
+ PACK_STRUCT_FIELD(u32_t reserved);
+ /* Options follow. */
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+/** Router advertisement message header. */
+#define ND6_RA_FLAG_MANAGED_ADDR_CONFIG (0x80)
+#define ND6_RA_FLAG_OTHER_CONFIG (0x40)
+#define ND6_RA_FLAG_HOME_AGENT (0x20)
+#define ND6_RA_PREFERENCE_MASK (0x18)
+#define ND6_RA_PREFERENCE_HIGH (0x08)
+#define ND6_RA_PREFERENCE_MEDIUM (0x00)
+#define ND6_RA_PREFERENCE_LOW (0x18)
+#define ND6_RA_PREFERENCE_DISABLED (0x10)
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct ra_header {
+ PACK_STRUCT_FLD_8(u8_t type);
+ PACK_STRUCT_FLD_8(u8_t code);
+ PACK_STRUCT_FIELD(u16_t chksum);
+ PACK_STRUCT_FLD_8(u8_t current_hop_limit);
+ PACK_STRUCT_FLD_8(u8_t flags);
+ PACK_STRUCT_FIELD(u16_t router_lifetime);
+ PACK_STRUCT_FIELD(u32_t reachable_time);
+ PACK_STRUCT_FIELD(u32_t retrans_timer);
+ /* Options follow. */
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+/** Redirect message header. */
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct redirect_header {
+ PACK_STRUCT_FLD_8(u8_t type);
+ PACK_STRUCT_FLD_8(u8_t code);
+ PACK_STRUCT_FIELD(u16_t chksum);
+ PACK_STRUCT_FIELD(u32_t reserved);
+ PACK_STRUCT_FLD_S(ip6_addr_p_t target_address);
+ PACK_STRUCT_FLD_S(ip6_addr_p_t destination_address);
+ /* Options follow. */
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+/** Link-layer address option. */
+#define ND6_OPTION_TYPE_SOURCE_LLADDR (0x01)
+#define ND6_OPTION_TYPE_TARGET_LLADDR (0x02)
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct lladdr_option {
+ PACK_STRUCT_FLD_8(u8_t type);
+ PACK_STRUCT_FLD_8(u8_t length);
+ PACK_STRUCT_FLD_8(u8_t addr[NETIF_MAX_HWADDR_LEN]);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+/** Prefix information option. */
+#define ND6_OPTION_TYPE_PREFIX_INFO (0x03)
+#define ND6_PREFIX_FLAG_ON_LINK (0x80)
+#define ND6_PREFIX_FLAG_AUTONOMOUS (0x40)
+#define ND6_PREFIX_FLAG_ROUTER_ADDRESS (0x20)
+#define ND6_PREFIX_FLAG_SITE_PREFIX (0x10)
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct prefix_option {
+ PACK_STRUCT_FLD_8(u8_t type);
+ PACK_STRUCT_FLD_8(u8_t length);
+ PACK_STRUCT_FLD_8(u8_t prefix_length);
+ PACK_STRUCT_FLD_8(u8_t flags);
+ PACK_STRUCT_FIELD(u32_t valid_lifetime);
+ PACK_STRUCT_FIELD(u32_t preferred_lifetime);
+ PACK_STRUCT_FLD_8(u8_t reserved2[3]);
+ PACK_STRUCT_FLD_8(u8_t site_prefix_length);
+ PACK_STRUCT_FLD_S(ip6_addr_p_t prefix);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+/** Redirected header option. */
+#define ND6_OPTION_TYPE_REDIR_HDR (0x04)
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct redirected_header_option {
+ PACK_STRUCT_FLD_8(u8_t type);
+ PACK_STRUCT_FLD_8(u8_t length);
+ PACK_STRUCT_FLD_8(u8_t reserved[6]);
+ /* Portion of redirected packet follows. */
+ /* PACK_STRUCT_FLD_8(u8_t redirected[8]); */
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+/** MTU option. */
+#define ND6_OPTION_TYPE_MTU (0x05)
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct mtu_option {
+ PACK_STRUCT_FLD_8(u8_t type);
+ PACK_STRUCT_FLD_8(u8_t length);
+ PACK_STRUCT_FIELD(u16_t reserved);
+ PACK_STRUCT_FIELD(u32_t mtu);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+/** Route information option. */
+#define ND6_OPTION_TYPE_ROUTE_INFO (24)
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct route_option {
+ PACK_STRUCT_FLD_8(u8_t type);
+ PACK_STRUCT_FLD_8(u8_t length);
+ PACK_STRUCT_FLD_8(u8_t prefix_length);
+ PACK_STRUCT_FLD_8(u8_t preference);
+ PACK_STRUCT_FIELD(u32_t route_lifetime);
+ PACK_STRUCT_FLD_S(ip6_addr_p_t prefix);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+/** Recursive DNS Server Option. */
+#if LWIP_ND6_RDNSS_MAX_DNS_SERVERS
+#define LWIP_RDNSS_OPTION_MAX_SERVERS LWIP_ND6_RDNSS_MAX_DNS_SERVERS
+#else
+#define LWIP_RDNSS_OPTION_MAX_SERVERS 1
+#endif
+#define ND6_OPTION_TYPE_RDNSS (25)
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct rdnss_option {
+ PACK_STRUCT_FLD_8(u8_t type);
+ PACK_STRUCT_FLD_8(u8_t length);
+ PACK_STRUCT_FIELD(u16_t reserved);
+ PACK_STRUCT_FIELD(u32_t lifetime);
+ PACK_STRUCT_FLD_S(ip6_addr_p_t rdnss_address[LWIP_RDNSS_OPTION_MAX_SERVERS]);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_PROT_ND6_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/prot/tcp.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/prot/tcp.h
new file mode 100644
index 0000000..67fe7b9
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/prot/tcp.h
@@ -0,0 +1,97 @@
+/**
+ * @file
+ * TCP protocol definitions
+ */
+
+/*
+ * 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_HDR_PROT_TCP_H
+#define LWIP_HDR_PROT_TCP_H
+
+#include "lwip/arch.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Length of the TCP header, excluding options. */
+#define TCP_HLEN 20
+
+/* Fields are (of course) in network byte order.
+ * Some fields are converted to host byte order in tcp_input().
+ */
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct tcp_hdr {
+ PACK_STRUCT_FIELD(u16_t src);
+ PACK_STRUCT_FIELD(u16_t dest);
+ PACK_STRUCT_FIELD(u32_t seqno);
+ PACK_STRUCT_FIELD(u32_t ackno);
+ PACK_STRUCT_FIELD(u16_t _hdrlen_rsvd_flags);
+ PACK_STRUCT_FIELD(u16_t wnd);
+ PACK_STRUCT_FIELD(u16_t chksum);
+ PACK_STRUCT_FIELD(u16_t urgp);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+/* TCP header flags bits */
+#define TCP_FIN 0x01U
+#define TCP_SYN 0x02U
+#define TCP_RST 0x04U
+#define TCP_PSH 0x08U
+#define TCP_ACK 0x10U
+#define TCP_URG 0x20U
+#define TCP_ECE 0x40U
+#define TCP_CWR 0x80U
+/* Valid TCP header flags */
+#define TCP_FLAGS 0x3fU
+
+#define TCPH_HDRLEN(phdr) ((u16_t)(lwip_ntohs((phdr)->_hdrlen_rsvd_flags) >> 12))
+#define TCPH_FLAGS(phdr) ((u16_t)(lwip_ntohs((phdr)->_hdrlen_rsvd_flags) & TCP_FLAGS))
+
+#define TCPH_HDRLEN_SET(phdr, len) (phdr)->_hdrlen_rsvd_flags = lwip_htons(((len) << 12) | TCPH_FLAGS(phdr))
+#define TCPH_FLAGS_SET(phdr, flags) (phdr)->_hdrlen_rsvd_flags = (((phdr)->_hdrlen_rsvd_flags & PP_HTONS(~TCP_FLAGS)) | lwip_htons(flags))
+#define TCPH_HDRLEN_FLAGS_SET(phdr, len, flags) (phdr)->_hdrlen_rsvd_flags = (u16_t)(lwip_htons((u16_t)((len) << 12) | (flags)))
+
+#define TCPH_SET_FLAG(phdr, flags ) (phdr)->_hdrlen_rsvd_flags = ((phdr)->_hdrlen_rsvd_flags | lwip_htons(flags))
+#define TCPH_UNSET_FLAG(phdr, flags) (phdr)->_hdrlen_rsvd_flags = ((phdr)->_hdrlen_rsvd_flags & ~lwip_htons(flags))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_PROT_TCP_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/prot/udp.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/prot/udp.h
new file mode 100644
index 0000000..664f19a
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/prot/udp.h
@@ -0,0 +1,68 @@
+/**
+ * @file
+ * UDP protocol definitions
+ */
+
+/*
+ * 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_HDR_PROT_UDP_H
+#define LWIP_HDR_PROT_UDP_H
+
+#include "lwip/arch.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define UDP_HLEN 8
+
+/* Fields are (of course) in network byte order. */
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct udp_hdr {
+ PACK_STRUCT_FIELD(u16_t src);
+ PACK_STRUCT_FIELD(u16_t dest); /* src/dest UDP ports */
+ PACK_STRUCT_FIELD(u16_t len);
+ PACK_STRUCT_FIELD(u16_t chksum);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_PROT_UDP_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/raw.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/raw.h
new file mode 100644
index 0000000..30aa147
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/raw.h
@@ -0,0 +1,118 @@
+/**
+ * @file
+ * raw API (to be used from TCPIP thread)\n
+ * See also @ref raw_raw
+ */
+
+/*
+ * 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_HDR_RAW_H
+#define LWIP_HDR_RAW_H
+
+#include "lwip/opt.h"
+
+#if LWIP_RAW /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/pbuf.h"
+#include "lwip/def.h"
+#include "lwip/ip.h"
+#include "lwip/ip_addr.h"
+#include "lwip/ip6_addr.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct raw_pcb;
+
+/** Function prototype for raw pcb receive callback functions.
+ * @param arg user supplied argument (raw_pcb.recv_arg)
+ * @param pcb the raw_pcb which received data
+ * @param p the packet buffer that was received
+ * @param addr the remote IP address from which the packet was received
+ * @return 1 if the packet was 'eaten' (aka. deleted),
+ * 0 if the packet lives on
+ * If returning 1, the callback is responsible for freeing the pbuf
+ * if it's not used any more.
+ */
+typedef u8_t (*raw_recv_fn)(void *arg, struct raw_pcb *pcb, struct pbuf *p,
+ const ip_addr_t *addr);
+
+/** the RAW protocol control block */
+struct raw_pcb {
+ /* Common members of all PCB types */
+ IP_PCB;
+
+ struct raw_pcb *next;
+
+ u8_t protocol;
+
+ /** receive callback function */
+ raw_recv_fn recv;
+ /* user-supplied argument for the recv callback */
+ void *recv_arg;
+#if LWIP_IPV6
+ /* fields for handling checksum computations as per RFC3542. */
+ u16_t chksum_offset;
+ u8_t chksum_reqd;
+#endif
+};
+
+/* The following functions is the application layer interface to the
+ RAW code. */
+struct raw_pcb * raw_new (u8_t proto);
+struct raw_pcb * raw_new_ip_type(u8_t type, u8_t proto);
+void raw_remove (struct raw_pcb *pcb);
+err_t raw_bind (struct raw_pcb *pcb, const ip_addr_t *ipaddr);
+err_t raw_connect (struct raw_pcb *pcb, const ip_addr_t *ipaddr);
+
+err_t raw_sendto (struct raw_pcb *pcb, struct pbuf *p, const ip_addr_t *ipaddr);
+err_t raw_send (struct raw_pcb *pcb, struct pbuf *p);
+
+void raw_recv (struct raw_pcb *pcb, raw_recv_fn recv, void *recv_arg);
+
+/* The following functions are the lower layer interface to RAW. */
+u8_t raw_input (struct pbuf *p, struct netif *inp);
+#define raw_init() /* Compatibility define, no init needed. */
+
+void raw_netif_ip_addr_changed(const ip_addr_t* old_addr, const ip_addr_t* new_addr);
+
+/* for compatibility with older implementation */
+#define raw_new_ip6(proto) raw_new_ip_type(IPADDR_TYPE_V6, proto)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_RAW */
+
+#endif /* LWIP_HDR_RAW_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/sio.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/sio.h
new file mode 100644
index 0000000..7643e19
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/sio.h
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ */
+
+/*
+ * This is the interface to the platform specific serial IO module
+ * It needs to be implemented by those platforms which need SLIP or PPP
+ */
+
+#ifndef SIO_H
+#define SIO_H
+
+#include "lwip/arch.h"
+#include "lwip/opt.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* If you want to define sio_fd_t elsewhere or differently,
+ define this in your cc.h file. */
+#ifndef __sio_fd_t_defined
+typedef void * sio_fd_t;
+#endif
+
+/* The following functions can be defined to something else in your cc.h file
+ or be implemented in your custom sio.c file. */
+
+#ifndef sio_open
+/**
+ * Opens a serial device for communication.
+ *
+ * @param devnum device number
+ * @return handle to serial device if successful, NULL otherwise
+ */
+sio_fd_t sio_open(u8_t devnum);
+#endif
+
+#ifndef sio_send
+/**
+ * Sends a single character to the serial device.
+ *
+ * @param c character to send
+ * @param fd serial device handle
+ *
+ * @note This function will block until the character can be sent.
+ */
+void sio_send(u8_t c, sio_fd_t fd);
+#endif
+
+#ifndef sio_recv
+/**
+ * Receives a single character from the serial device.
+ *
+ * @param fd serial device handle
+ *
+ * @note This function will block until a character is received.
+ */
+u8_t sio_recv(sio_fd_t fd);
+#endif
+
+#ifndef sio_read
+/**
+ * Reads from the serial device.
+ *
+ * @param fd serial device handle
+ * @param data pointer to data buffer for receiving
+ * @param len maximum length (in bytes) of data to receive
+ * @return number of bytes actually received - may be 0 if aborted by sio_read_abort
+ *
+ * @note This function will block until data can be received. The blocking
+ * can be cancelled by calling sio_read_abort().
+ */
+u32_t sio_read(sio_fd_t fd, u8_t *data, u32_t len);
+#endif
+
+#ifndef sio_tryread
+/**
+ * Tries to read from the serial device. Same as sio_read but returns
+ * immediately if no data is available and never blocks.
+ *
+ * @param fd serial device handle
+ * @param data pointer to data buffer for receiving
+ * @param len maximum length (in bytes) of data to receive
+ * @return number of bytes actually received
+ */
+u32_t sio_tryread(sio_fd_t fd, u8_t *data, u32_t len);
+#endif
+
+#ifndef sio_write
+/**
+ * Writes to the serial device.
+ *
+ * @param fd serial device handle
+ * @param data pointer to data to send
+ * @param len length (in bytes) of data to send
+ * @return number of bytes actually sent
+ *
+ * @note This function will block until all data can be sent.
+ */
+u32_t sio_write(sio_fd_t fd, u8_t *data, u32_t len);
+#endif
+
+#ifndef sio_read_abort
+/**
+ * Aborts a blocking sio_read() call.
+ *
+ * @param fd serial device handle
+ */
+void sio_read_abort(sio_fd_t fd);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SIO_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/snmp.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/snmp.h
new file mode 100644
index 0000000..8704d0b
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/snmp.h
@@ -0,0 +1,213 @@
+/**
+ * @file
+ * SNMP support API for implementing netifs and statitics for MIB2
+ */
+
+/*
+ * 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: Dirk Ziegelmeier <dziegel@gmx.de>
+ *
+ */
+#ifndef LWIP_HDR_SNMP_H
+#define LWIP_HDR_SNMP_H
+
+#include "lwip/opt.h"
+#include "lwip/ip_addr.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct udp_pcb;
+struct netif;
+
+/**
+ * @defgroup netif_mib2 MIB2 statistics
+ * @ingroup netif
+ */
+
+/* MIB2 statistics functions */
+#if MIB2_STATS /* don't build if not configured for use in lwipopts.h */
+/**
+ * @ingroup netif_mib2
+ * @see RFC1213, "MIB-II, 6. Definitions"
+ */
+enum snmp_ifType {
+ snmp_ifType_other=1, /* none of the following */
+ snmp_ifType_regular1822,
+ snmp_ifType_hdh1822,
+ snmp_ifType_ddn_x25,
+ snmp_ifType_rfc877_x25,
+ snmp_ifType_ethernet_csmacd,
+ snmp_ifType_iso88023_csmacd,
+ snmp_ifType_iso88024_tokenBus,
+ snmp_ifType_iso88025_tokenRing,
+ snmp_ifType_iso88026_man,
+ snmp_ifType_starLan,
+ snmp_ifType_proteon_10Mbit,
+ snmp_ifType_proteon_80Mbit,
+ snmp_ifType_hyperchannel,
+ snmp_ifType_fddi,
+ snmp_ifType_lapb,
+ snmp_ifType_sdlc,
+ snmp_ifType_ds1, /* T-1 */
+ snmp_ifType_e1, /* european equiv. of T-1 */
+ snmp_ifType_basicISDN,
+ snmp_ifType_primaryISDN, /* proprietary serial */
+ snmp_ifType_propPointToPointSerial,
+ snmp_ifType_ppp,
+ snmp_ifType_softwareLoopback,
+ snmp_ifType_eon, /* CLNP over IP [11] */
+ snmp_ifType_ethernet_3Mbit,
+ snmp_ifType_nsip, /* XNS over IP */
+ snmp_ifType_slip, /* generic SLIP */
+ snmp_ifType_ultra, /* ULTRA technologies */
+ snmp_ifType_ds3, /* T-3 */
+ snmp_ifType_sip, /* SMDS */
+ snmp_ifType_frame_relay
+};
+
+/** This macro has a precision of ~49 days because sys_now returns u32_t. \#define your own if you want ~490 days. */
+#ifndef MIB2_COPY_SYSUPTIME_TO
+#define MIB2_COPY_SYSUPTIME_TO(ptrToVal) (*(ptrToVal) = (sys_now() / 10))
+#endif
+
+/**
+ * @ingroup netif_mib2
+ * Increment stats member for SNMP MIB2 stats (struct stats_mib2_netif_ctrs)
+ */
+#define MIB2_STATS_NETIF_INC(n, x) do { ++(n)->mib2_counters.x; } while(0)
+/**
+ * @ingroup netif_mib2
+ * Add value to stats member for SNMP MIB2 stats (struct stats_mib2_netif_ctrs)
+ */
+#define MIB2_STATS_NETIF_ADD(n, x, val) do { (n)->mib2_counters.x += (val); } while(0)
+
+/**
+ * @ingroup netif_mib2
+ * Init MIB2 statistic counters in netif
+ * @param netif Netif to init
+ * @param type one of enum @ref snmp_ifType
+ * @param speed your link speed here (units: bits per second)
+ */
+#define MIB2_INIT_NETIF(netif, type, speed) do { \
+ (netif)->link_type = (type); \
+ (netif)->link_speed = (speed);\
+ (netif)->ts = 0; \
+ (netif)->mib2_counters.ifinoctets = 0; \
+ (netif)->mib2_counters.ifinucastpkts = 0; \
+ (netif)->mib2_counters.ifinnucastpkts = 0; \
+ (netif)->mib2_counters.ifindiscards = 0; \
+ (netif)->mib2_counters.ifinerrors = 0; \
+ (netif)->mib2_counters.ifinunknownprotos = 0; \
+ (netif)->mib2_counters.ifoutoctets = 0; \
+ (netif)->mib2_counters.ifoutucastpkts = 0; \
+ (netif)->mib2_counters.ifoutnucastpkts = 0; \
+ (netif)->mib2_counters.ifoutdiscards = 0; \
+ (netif)->mib2_counters.ifouterrors = 0; } while(0)
+#else /* MIB2_STATS */
+#ifndef MIB2_COPY_SYSUPTIME_TO
+#define MIB2_COPY_SYSUPTIME_TO(ptrToVal)
+#endif
+#define MIB2_INIT_NETIF(netif, type, speed)
+#define MIB2_STATS_NETIF_INC(n, x)
+#define MIB2_STATS_NETIF_ADD(n, x, val)
+#endif /* MIB2_STATS */
+
+/* LWIP MIB2 callbacks */
+#if LWIP_MIB2_CALLBACKS /* don't build if not configured for use in lwipopts.h */
+/* network interface */
+void mib2_netif_added(struct netif *ni);
+void mib2_netif_removed(struct netif *ni);
+
+#if LWIP_IPV4 && LWIP_ARP
+/* ARP (for atTable and ipNetToMediaTable) */
+void mib2_add_arp_entry(struct netif *ni, ip4_addr_t *ip);
+void mib2_remove_arp_entry(struct netif *ni, ip4_addr_t *ip);
+#else /* LWIP_IPV4 && LWIP_ARP */
+#define mib2_add_arp_entry(ni,ip)
+#define mib2_remove_arp_entry(ni,ip)
+#endif /* LWIP_IPV4 && LWIP_ARP */
+
+/* IP */
+#if LWIP_IPV4
+void mib2_add_ip4(struct netif *ni);
+void mib2_remove_ip4(struct netif *ni);
+void mib2_add_route_ip4(u8_t dflt, struct netif *ni);
+void mib2_remove_route_ip4(u8_t dflt, struct netif *ni);
+#endif /* LWIP_IPV4 */
+
+/* UDP */
+#if LWIP_UDP
+void mib2_udp_bind(struct udp_pcb *pcb);
+void mib2_udp_unbind(struct udp_pcb *pcb);
+#endif /* LWIP_UDP */
+
+#else /* LWIP_MIB2_CALLBACKS */
+/* LWIP_MIB2_CALLBACKS support not available */
+/* define everything to be empty */
+
+/* network interface */
+#define mib2_netif_added(ni)
+#define mib2_netif_removed(ni)
+
+/* ARP */
+#define mib2_add_arp_entry(ni,ip)
+#define mib2_remove_arp_entry(ni,ip)
+
+/* IP */
+#define mib2_add_ip4(ni)
+#define mib2_remove_ip4(ni)
+#define mib2_add_route_ip4(dflt, ni)
+#define mib2_remove_route_ip4(dflt, ni)
+
+/* UDP */
+#define mib2_udp_bind(pcb)
+#define mib2_udp_unbind(pcb)
+#endif /* LWIP_MIB2_CALLBACKS */
+
+/* for source-code compatibility reasons only, can be removed (not used internally) */
+#define NETIF_INIT_SNMP MIB2_INIT_NETIF
+#define snmp_add_ifinoctets(ni,value) MIB2_STATS_NETIF_ADD(ni, ifinoctets, value)
+#define snmp_inc_ifinucastpkts(ni) MIB2_STATS_NETIF_INC(ni, ifinucastpkts)
+#define snmp_inc_ifinnucastpkts(ni) MIB2_STATS_NETIF_INC(ni, ifinnucastpkts)
+#define snmp_inc_ifindiscards(ni) MIB2_STATS_NETIF_INC(ni, ifindiscards)
+#define snmp_inc_ifinerrors(ni) MIB2_STATS_NETIF_INC(ni, ifinerrors)
+#define snmp_inc_ifinunknownprotos(ni) MIB2_STATS_NETIF_INC(ni, ifinunknownprotos)
+#define snmp_add_ifoutoctets(ni,value) MIB2_STATS_NETIF_ADD(ni, ifoutoctets, value)
+#define snmp_inc_ifoutucastpkts(ni) MIB2_STATS_NETIF_INC(ni, ifoutucastpkts)
+#define snmp_inc_ifoutnucastpkts(ni) MIB2_STATS_NETIF_INC(ni, ifoutnucastpkts)
+#define snmp_inc_ifoutdiscards(ni) MIB2_STATS_NETIF_INC(ni, ifoutdiscards)
+#define snmp_inc_ifouterrors(ni) MIB2_STATS_NETIF_INC(ni, ifouterrors)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_SNMP_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/snmp_asn1.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/snmp_asn1.h
new file mode 100644
index 0000000..b05c8e7
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/snmp_asn1.h
@@ -0,0 +1,101 @@
+/**
+ * @file
+ * Abstract Syntax Notation One (ISO 8824, 8825) codec.
+ */
+
+/*
+ * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Christiaan Simons <christiaan.simons@axon.tv>
+ */
+
+#ifndef LWIP_HDR_SNMP_ASN1_H
+#define LWIP_HDR_SNMP_ASN1_H
+
+#include "lwip/opt.h"
+#include "lwip/err.h"
+#include "lwip/pbuf.h"
+#include "lwip/snmp.h"
+
+#if LWIP_SNMP
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define SNMP_ASN1_UNIV (0) /* (!0x80 | !0x40) */
+#define SNMP_ASN1_APPLIC (0x40) /* (!0x80 | 0x40) */
+#define SNMP_ASN1_CONTXT (0x80) /* ( 0x80 | !0x40) */
+
+#define SNMP_ASN1_CONSTR (0x20) /* ( 0x20) */
+#define SNMP_ASN1_PRIMIT (0) /* (!0x20) */
+
+/* universal tags */
+#define SNMP_ASN1_INTEG 2
+#define SNMP_ASN1_OC_STR 4
+#define SNMP_ASN1_NUL 5
+#define SNMP_ASN1_OBJ_ID 6
+#define SNMP_ASN1_SEQ 16
+
+/* application specific (SNMP) tags */
+#define SNMP_ASN1_IPADDR 0 /* octet string size(4) */
+#define SNMP_ASN1_COUNTER 1 /* u32_t */
+#define SNMP_ASN1_GAUGE 2 /* u32_t */
+#define SNMP_ASN1_TIMETICKS 3 /* u32_t */
+#define SNMP_ASN1_OPAQUE 4 /* octet string */
+
+/* context specific (SNMP) tags */
+#define SNMP_ASN1_PDU_GET_REQ 0
+#define SNMP_ASN1_PDU_GET_NEXT_REQ 1
+#define SNMP_ASN1_PDU_GET_RESP 2
+#define SNMP_ASN1_PDU_SET_REQ 3
+#define SNMP_ASN1_PDU_TRAP 4
+
+err_t snmp_asn1_dec_type(struct pbuf *p, u16_t ofs, u8_t *type);
+err_t snmp_asn1_dec_length(struct pbuf *p, u16_t ofs, u8_t *octets_used, u16_t *length);
+err_t snmp_asn1_dec_u32t(struct pbuf *p, u16_t ofs, u16_t len, u32_t *value);
+err_t snmp_asn1_dec_s32t(struct pbuf *p, u16_t ofs, u16_t len, s32_t *value);
+err_t snmp_asn1_dec_oid(struct pbuf *p, u16_t ofs, u16_t len, struct snmp_obj_id *oid);
+err_t snmp_asn1_dec_raw(struct pbuf *p, u16_t ofs, u16_t len, u16_t raw_len, u8_t *raw);
+
+void snmp_asn1_enc_length_cnt(u16_t length, u8_t *octets_needed);
+void snmp_asn1_enc_u32t_cnt(u32_t value, u16_t *octets_needed);
+void snmp_asn1_enc_s32t_cnt(s32_t value, u16_t *octets_needed);
+void snmp_asn1_enc_oid_cnt(u8_t ident_len, const s32_t *ident, u16_t *octets_needed);
+err_t snmp_asn1_enc_type(struct pbuf *p, u16_t ofs, u8_t type);
+err_t snmp_asn1_enc_length(struct pbuf *p, u16_t ofs, u16_t length);
+err_t snmp_asn1_enc_u32t(struct pbuf *p, u16_t ofs, u16_t octets_needed, u32_t value);
+err_t snmp_asn1_enc_s32t(struct pbuf *p, u16_t ofs, u16_t octets_needed, s32_t value);
+err_t snmp_asn1_enc_oid(struct pbuf *p, u16_t ofs, u8_t ident_len, const s32_t *ident);
+err_t snmp_asn1_enc_raw(struct pbuf *p, u16_t ofs, u16_t raw_len, u8_t *raw);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_SNMP */
+
+#endif /* LWIP_HDR_SNMP_ASN1_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/snmp_msg.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/snmp_msg.h
new file mode 100644
index 0000000..847ddd9
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/snmp_msg.h
@@ -0,0 +1,321 @@
+/**
+ * @file
+ * SNMP Agent message handling structures.
+ */
+
+/*
+ * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Christiaan Simons <christiaan.simons@axon.tv>
+ */
+
+#ifndef LWIP_HDR_SNMP_MSG_H
+#define LWIP_HDR_SNMP_MSG_H
+
+#include "lwip/opt.h"
+#include "lwip/snmp.h"
+#include "lwip/snmp_structs.h"
+#include "lwip/ip_addr.h"
+#include "lwip/err.h"
+
+#if LWIP_SNMP
+
+#if SNMP_PRIVATE_MIB
+/* When using a private MIB, you have to create a file 'private_mib.h' that contains
+ * a 'struct mib_array_node mib_private' which contains your MIB. */
+#include "private_mib.h"
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* The listen port of the SNMP agent. Clients have to make their requests to
+ this port. Most standard clients won't work if you change this! */
+#ifndef SNMP_IN_PORT
+#define SNMP_IN_PORT 161
+#endif
+/* The remote port the SNMP agent sends traps to. Most standard trap sinks won't
+ work if you change this! */
+#ifndef SNMP_TRAP_PORT
+#define SNMP_TRAP_PORT 162
+#endif
+
+#define SNMP_ES_NOERROR 0
+#define SNMP_ES_TOOBIG 1
+#define SNMP_ES_NOSUCHNAME 2
+#define SNMP_ES_BADVALUE 3
+#define SNMP_ES_READONLY 4
+#define SNMP_ES_GENERROR 5
+
+#define SNMP_GENTRAP_COLDSTART 0
+#define SNMP_GENTRAP_WARMSTART 1
+#define SNMP_GENTRAP_AUTHFAIL 4
+#define SNMP_GENTRAP_ENTERPRISESPC 6
+
+struct snmp_varbind
+{
+ /* next pointer, NULL for last in list */
+ struct snmp_varbind *next;
+ /* previous pointer, NULL for first in list */
+ struct snmp_varbind *prev;
+
+ /* object identifier length (in s32_t) */
+ u8_t ident_len;
+ /* object identifier array */
+ s32_t *ident;
+
+ /* object value ASN1 type */
+ u8_t value_type;
+ /* object value length (in u8_t) */
+ u8_t value_len;
+ /* object value */
+ void *value;
+
+ /* encoding varbind seq length length */
+ u8_t seqlenlen;
+ /* encoding object identifier length length */
+ u8_t olenlen;
+ /* encoding object value length length */
+ u8_t vlenlen;
+ /* encoding varbind seq length */
+ u16_t seqlen;
+ /* encoding object identifier length */
+ u16_t olen;
+ /* encoding object value length */
+ u16_t vlen;
+};
+
+struct snmp_varbind_root
+{
+ struct snmp_varbind *head;
+ struct snmp_varbind *tail;
+ /* number of variable bindings in list */
+ u8_t count;
+ /* encoding varbind-list seq length length */
+ u8_t seqlenlen;
+ /* encoding varbind-list seq length */
+ u16_t seqlen;
+};
+
+/** output response message header length fields */
+struct snmp_resp_header_lengths
+{
+ /* encoding error-index length length */
+ u8_t erridxlenlen;
+ /* encoding error-status length length */
+ u8_t errstatlenlen;
+ /* encoding request id length length */
+ u8_t ridlenlen;
+ /* encoding pdu length length */
+ u8_t pdulenlen;
+ /* encoding community length length */
+ u8_t comlenlen;
+ /* encoding version length length */
+ u8_t verlenlen;
+ /* encoding sequence length length */
+ u8_t seqlenlen;
+
+ /* encoding error-index length */
+ u16_t erridxlen;
+ /* encoding error-status length */
+ u16_t errstatlen;
+ /* encoding request id length */
+ u16_t ridlen;
+ /* encoding pdu length */
+ u16_t pdulen;
+ /* encoding community length */
+ u16_t comlen;
+ /* encoding version length */
+ u16_t verlen;
+ /* encoding sequence length */
+ u16_t seqlen;
+};
+
+/** output response message header length fields */
+struct snmp_trap_header_lengths
+{
+ /* encoding timestamp length length */
+ u8_t tslenlen;
+ /* encoding specific-trap length length */
+ u8_t strplenlen;
+ /* encoding generic-trap length length */
+ u8_t gtrplenlen;
+ /* encoding agent-addr length length */
+ u8_t aaddrlenlen;
+ /* encoding enterprise-id length length */
+ u8_t eidlenlen;
+ /* encoding pdu length length */
+ u8_t pdulenlen;
+ /* encoding community length length */
+ u8_t comlenlen;
+ /* encoding version length length */
+ u8_t verlenlen;
+ /* encoding sequence length length */
+ u8_t seqlenlen;
+
+ /* encoding timestamp length */
+ u16_t tslen;
+ /* encoding specific-trap length */
+ u16_t strplen;
+ /* encoding generic-trap length */
+ u16_t gtrplen;
+ /* encoding agent-addr length */
+ u16_t aaddrlen;
+ /* encoding enterprise-id length */
+ u16_t eidlen;
+ /* encoding pdu length */
+ u16_t pdulen;
+ /* encoding community length */
+ u16_t comlen;
+ /* encoding version length */
+ u16_t verlen;
+ /* encoding sequence length */
+ u16_t seqlen;
+};
+
+/* Accepting new SNMP messages. */
+#define SNMP_MSG_EMPTY 0
+/* Search for matching object for variable binding. */
+#define SNMP_MSG_SEARCH_OBJ 1
+/* Perform SNMP operation on in-memory object.
+ Pass-through states, for symmetry only. */
+#define SNMP_MSG_INTERNAL_GET_OBJDEF 2
+#define SNMP_MSG_INTERNAL_GET_VALUE 3
+#define SNMP_MSG_INTERNAL_SET_TEST 4
+#define SNMP_MSG_INTERNAL_GET_OBJDEF_S 5
+#define SNMP_MSG_INTERNAL_SET_VALUE 6
+/* Perform SNMP operation on object located externally.
+ In theory this could be used for building a proxy agent.
+ Practical use is for an enterprise spc. app. gateway. */
+#define SNMP_MSG_EXTERNAL_GET_OBJDEF 7
+#define SNMP_MSG_EXTERNAL_GET_VALUE 8
+#define SNMP_MSG_EXTERNAL_SET_TEST 9
+#define SNMP_MSG_EXTERNAL_GET_OBJDEF_S 10
+#define SNMP_MSG_EXTERNAL_SET_VALUE 11
+
+#define SNMP_COMMUNITY_STR_LEN 64
+struct snmp_msg_pstat
+{
+ /* lwIP local port (161) binding */
+ struct udp_pcb *pcb;
+ /* source IP address */
+ ip_addr_t sip;
+ /* source UDP port */
+ u16_t sp;
+ /* request type */
+ u8_t rt;
+ /* request ID */
+ s32_t rid;
+ /* error status */
+ s32_t error_status;
+ /* error index */
+ s32_t error_index;
+ /* community name (zero terminated) */
+ u8_t community[SNMP_COMMUNITY_STR_LEN + 1];
+ /* community string length (exclusive zero term) */
+ u8_t com_strlen;
+ /* one out of MSG_EMPTY, MSG_DEMUX, MSG_INTERNAL, MSG_EXTERNAL_x */
+ u8_t state;
+ /* saved arguments for MSG_EXTERNAL_x */
+ struct mib_external_node *ext_mib_node;
+ struct snmp_name_ptr ext_name_ptr;
+ struct obj_def ext_object_def;
+ struct snmp_obj_id ext_oid;
+ /* index into input variable binding list */
+ u8_t vb_idx;
+ /* ptr into input variable binding list */
+ struct snmp_varbind *vb_ptr;
+ /* list of variable bindings from input */
+ struct snmp_varbind_root invb;
+ /* list of variable bindings to output */
+ struct snmp_varbind_root outvb;
+ /* output response lengths used in ASN encoding */
+ struct snmp_resp_header_lengths rhl;
+};
+
+struct snmp_msg_trap
+{
+ /* lwIP local port (161) binding */
+ struct udp_pcb *pcb;
+ /* destination IP address in network order */
+ ip_addr_t dip;
+
+ /* source enterprise ID (sysObjectID) */
+ const struct snmp_obj_id *enterprise;
+ /* source IP address, raw network order format */
+ u8_t sip_raw[4];
+ /* generic trap code */
+ u32_t gen_trap;
+ /* specific trap code */
+ u32_t spc_trap;
+ /* timestamp */
+ u32_t ts;
+ /* list of variable bindings to output */
+ struct snmp_varbind_root outvb;
+ /* output trap lengths used in ASN encoding */
+ struct snmp_trap_header_lengths thl;
+};
+
+/** Agent Version constant, 0 = v1 oddity */
+extern const s32_t snmp_version;
+/** Agent community string */
+extern const char *snmp_community;
+#if SNMP_COMMUNITY_EXT
+/** Agent community string for write access */
+extern const char *snmp_community_write;
+/** Agent community string for sending traps */
+extern const char *snmp_community_trap;
+#endif /* SNMP_COMMUNITY_EXT */
+
+extern struct snmp_msg_trap trap_msg;
+
+/** Agent setup, start listening to port 161. */
+void snmp_init(void);
+void snmp_trap_dst_enable(u8_t dst_idx, u8_t enable);
+void snmp_trap_dst_ip_set(u8_t dst_idx, ip_addr_t *dst);
+
+/** Varbind-list functions. */
+struct snmp_varbind* snmp_varbind_alloc(struct snmp_obj_id *oid, u8_t type, u8_t len);
+void snmp_varbind_free(struct snmp_varbind *vb);
+void snmp_varbind_list_free(struct snmp_varbind_root *root);
+void snmp_varbind_tail_add(struct snmp_varbind_root *root, struct snmp_varbind *vb);
+struct snmp_varbind* snmp_varbind_tail_remove(struct snmp_varbind_root *root);
+
+/** Handle an internal (recv) or external (private response) event. */
+void snmp_msg_event(u8_t request_id);
+err_t snmp_send_response(struct snmp_msg_pstat *m_stat);
+err_t snmp_send_trap(s8_t generic_trap, const struct snmp_obj_id *eoid, s32_t specific_trap);
+void snmp_coldstart_trap(void);
+void snmp_authfail_trap(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_SNMP */
+
+#endif /* LWIP_HDR_SNMP_MSG_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/snmp_structs.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/snmp_structs.h
new file mode 100644
index 0000000..b69160c
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/snmp_structs.h
@@ -0,0 +1,268 @@
+/**
+ * @file
+ * Generic MIB tree structures.
+ *
+ * @todo namespace prefixes
+ */
+
+/*
+ * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Christiaan Simons <christiaan.simons@axon.tv>
+ */
+
+#ifndef LWIP_HDR_SNMP_STRUCTS_H
+#define LWIP_HDR_SNMP_STRUCTS_H
+
+#include "lwip/opt.h"
+
+#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/snmp.h"
+
+#if SNMP_PRIVATE_MIB
+/* When using a private MIB, you have to create a file 'private_mib.h' that contains
+ * a 'struct mib_array_node mib_private' which contains your MIB. */
+#include "private_mib.h"
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* MIB object instance */
+#define MIB_OBJECT_NONE 0
+#define MIB_OBJECT_SCALAR 1
+#define MIB_OBJECT_TAB 2
+
+/* MIB access types */
+#define MIB_ACCESS_READ 1
+#define MIB_ACCESS_WRITE 2
+
+/* MIB object access */
+#define MIB_OBJECT_READ_ONLY MIB_ACCESS_READ
+#define MIB_OBJECT_READ_WRITE (MIB_ACCESS_READ | MIB_ACCESS_WRITE)
+#define MIB_OBJECT_WRITE_ONLY MIB_ACCESS_WRITE
+#define MIB_OBJECT_NOT_ACCESSIBLE 0
+
+/** object definition returned by (get_object_def)() */
+struct obj_def
+{
+ /* MIB_OBJECT_NONE (0), MIB_OBJECT_SCALAR (1), MIB_OBJECT_TAB (2) */
+ u8_t instance;
+ /* 0 read-only, 1 read-write, 2 write-only, 3 not-accessible */
+ u8_t access;
+ /* ASN type for this object */
+ u8_t asn_type;
+ /* value length (host length) */
+ u16_t v_len;
+ /* length of instance part of supplied object identifier */
+ u8_t id_inst_len;
+ /* instance part of supplied object identifier */
+ s32_t *id_inst_ptr;
+};
+
+struct snmp_name_ptr
+{
+ u8_t ident_len;
+ s32_t *ident;
+};
+
+/** MIB const scalar (.0) node */
+#define MIB_NODE_SC 0x01
+/** MIB const array node */
+#define MIB_NODE_AR 0x02
+/** MIB array node (mem_malloced from RAM) */
+#define MIB_NODE_RA 0x03
+/** MIB list root node (mem_malloced from RAM) */
+#define MIB_NODE_LR 0x04
+/** MIB node for external objects */
+#define MIB_NODE_EX 0x05
+
+/** node "base class" layout, the mandatory fields for a node */
+struct mib_node
+{
+ /** returns struct obj_def for the given object identifier */
+ void (*get_object_def)(u8_t ident_len, s32_t *ident, struct obj_def *od);
+ /** returns object value for the given object identifier,
+ @note the caller must allocate at least len bytes for the value */
+ void (*get_value)(struct obj_def *od, u16_t len, void *value);
+ /** tests length and/or range BEFORE setting */
+ u8_t (*set_test)(struct obj_def *od, u16_t len, void *value);
+ /** sets object value, only to be called when set_test() */
+ void (*set_value)(struct obj_def *od, u16_t len, void *value);
+ /** One out of MIB_NODE_AR, MIB_NODE_LR or MIB_NODE_EX */
+ u8_t node_type;
+ /* array or max list length */
+ u16_t maxlength;
+};
+
+/** derived node for scalars .0 index */
+typedef struct mib_node mib_scalar_node;
+
+/** derived node, points to a fixed size const array
+ of sub-identifiers plus a 'child' pointer */
+struct mib_array_node
+{
+ /* inherited "base class" members */
+ void (*get_object_def)(u8_t ident_len, s32_t *ident, struct obj_def *od);
+ void (*get_value)(struct obj_def *od, u16_t len, void *value);
+ u8_t (*set_test)(struct obj_def *od, u16_t len, void *value);
+ void (*set_value)(struct obj_def *od, u16_t len, void *value);
+
+ u8_t node_type;
+ u16_t maxlength;
+
+ /* additional struct members */
+ const s32_t *objid;
+ struct mib_node* const *nptr;
+};
+
+/** derived node, points to a fixed size mem_malloced array
+ of sub-identifiers plus a 'child' pointer */
+struct mib_ram_array_node
+{
+ /* inherited "base class" members */
+ void (*get_object_def)(u8_t ident_len, s32_t *ident, struct obj_def *od);
+ void (*get_value)(struct obj_def *od, u16_t len, void *value);
+ u8_t (*set_test)(struct obj_def *od, u16_t len, void *value);
+ void (*set_value)(struct obj_def *od, u16_t len, void *value);
+
+ u8_t node_type;
+ u16_t maxlength;
+
+ /* aditional struct members */
+ s32_t *objid;
+ struct mib_node **nptr;
+};
+
+struct mib_list_node
+{
+ struct mib_list_node *prev;
+ struct mib_list_node *next;
+ s32_t objid;
+ struct mib_node *nptr;
+};
+
+/** derived node, points to a doubly linked list
+ of sub-identifiers plus a 'child' pointer */
+struct mib_list_rootnode
+{
+ /* inherited "base class" members */
+ void (*get_object_def)(u8_t ident_len, s32_t *ident, struct obj_def *od);
+ void (*get_value)(struct obj_def *od, u16_t len, void *value);
+ u8_t (*set_test)(struct obj_def *od, u16_t len, void *value);
+ void (*set_value)(struct obj_def *od, u16_t len, void *value);
+
+ u8_t node_type;
+ u16_t maxlength;
+
+ /* additional struct members */
+ struct mib_list_node *head;
+ struct mib_list_node *tail;
+ /* counts list nodes in list */
+ u16_t count;
+};
+
+/** derived node, has access functions for mib object in external memory or device
+ using 'tree_level' and 'idx', with a range 0 .. (level_length() - 1) */
+struct mib_external_node
+{
+ /* inherited "base class" members */
+ void (*get_object_def)(u8_t ident_len, s32_t *ident, struct obj_def *od);
+ void (*get_value)(struct obj_def *od, u16_t len, void *value);
+ u8_t (*set_test)(struct obj_def *od, u16_t len, void *value);
+ void (*set_value)(struct obj_def *od, u16_t len, void *value);
+
+ u8_t node_type;
+ u16_t maxlength;
+
+ /* additional struct members */
+ /** points to an external (in memory) record of some sort of addressing
+ information, passed to and interpreted by the functions below */
+ void* addr_inf;
+ /** tree levels under this node */
+ u8_t tree_levels;
+ /** number of objects at this level */
+ u16_t (*level_length)(void* addr_inf, u8_t level);
+ /** compares object sub identifier with external id
+ return zero when equal, nonzero when unequal */
+ s32_t (*ident_cmp)(void* addr_inf, u8_t level, u16_t idx, s32_t sub_id);
+ void (*get_objid)(void* addr_inf, u8_t level, u16_t idx, s32_t *sub_id);
+
+ /** async Questions */
+ void (*get_object_def_q)(void* addr_inf, u8_t rid, u8_t ident_len, s32_t *ident);
+ void (*get_value_q)(u8_t rid, struct obj_def *od);
+ void (*set_test_q)(u8_t rid, struct obj_def *od);
+ void (*set_value_q)(u8_t rid, struct obj_def *od, u16_t len, void *value);
+ /** async Answers */
+ void (*get_object_def_a)(u8_t rid, u8_t ident_len, s32_t *ident, struct obj_def *od);
+ void (*get_value_a)(u8_t rid, struct obj_def *od, u16_t len, void *value);
+ u8_t (*set_test_a)(u8_t rid, struct obj_def *od, u16_t len, void *value);
+ void (*set_value_a)(u8_t rid, struct obj_def *od, u16_t len, void *value);
+ /** async Panic Close (agent returns error reply,
+ e.g. used for external transaction cleanup) */
+ void (*get_object_def_pc)(u8_t rid, u8_t ident_len, s32_t *ident);
+ void (*get_value_pc)(u8_t rid, struct obj_def *od);
+ void (*set_test_pc)(u8_t rid, struct obj_def *od);
+ void (*set_value_pc)(u8_t rid, struct obj_def *od);
+};
+
+/** export MIB tree from mib2.c */
+extern const struct mib_array_node internet;
+
+/** dummy function pointers for non-leaf MIB nodes from mib2.c */
+void noleafs_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od);
+void noleafs_get_value(struct obj_def *od, u16_t len, void *value);
+u8_t noleafs_set_test(struct obj_def *od, u16_t len, void *value);
+void noleafs_set_value(struct obj_def *od, u16_t len, void *value);
+
+void snmp_oidtoip(s32_t *ident, ip_addr_t *ip);
+void snmp_iptooid(ip_addr_t *ip, s32_t *ident);
+void snmp_ifindextonetif(s32_t ifindex, struct netif **netif);
+void snmp_netiftoifindex(struct netif *netif, s32_t *ifidx);
+
+struct mib_list_node* snmp_mib_ln_alloc(s32_t id);
+void snmp_mib_ln_free(struct mib_list_node *ln);
+struct mib_list_rootnode* snmp_mib_lrn_alloc(void);
+void snmp_mib_lrn_free(struct mib_list_rootnode *lrn);
+
+s8_t snmp_mib_node_insert(struct mib_list_rootnode *rn, s32_t objid, struct mib_list_node **insn);
+s8_t snmp_mib_node_find(struct mib_list_rootnode *rn, s32_t objid, struct mib_list_node **fn);
+struct mib_list_rootnode *snmp_mib_node_delete(struct mib_list_rootnode *rn, struct mib_list_node *n);
+
+struct mib_node* snmp_search_tree(struct mib_node *node, u8_t ident_len, s32_t *ident, struct snmp_name_ptr *np);
+struct mib_node* snmp_expand_tree(struct mib_node *node, u8_t ident_len, s32_t *ident, struct snmp_obj_id *oidret);
+u8_t snmp_iso_prefix_tst(u8_t ident_len, s32_t *ident);
+u8_t snmp_iso_prefix_expand(u8_t ident_len, s32_t *ident, struct snmp_obj_id *oidret);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_SNMP */
+
+#endif /* LWIP_HDR_SNMP_STRUCTS_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/sockets.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/sockets.h
new file mode 100644
index 0000000..2522056
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/sockets.h
@@ -0,0 +1,593 @@
+/**
+ * @file
+ * Socket API (to be used from non-TCPIP threads)
+ */
+
+/*
+ * 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_HDR_SOCKETS_H
+#define LWIP_HDR_SOCKETS_H
+
+#include "lwip/opt.h"
+
+#if LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/ip_addr.h"
+#include "lwip/err.h"
+#include "lwip/inet.h"
+#include "lwip/errno.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* If your port already typedef's sa_family_t, define SA_FAMILY_T_DEFINED
+ to prevent this code from redefining it. */
+#if !defined(sa_family_t) && !defined(SA_FAMILY_T_DEFINED)
+typedef u8_t sa_family_t;
+#endif
+/* If your port already typedef's in_port_t, define IN_PORT_T_DEFINED
+ to prevent this code from redefining it. */
+#if !defined(in_port_t) && !defined(IN_PORT_T_DEFINED)
+typedef u16_t in_port_t;
+#endif
+
+#if LWIP_IPV4
+/* members are in network byte order */
+struct sockaddr_in {
+ u8_t sin_len;
+ sa_family_t sin_family;
+ in_port_t sin_port;
+ struct in_addr sin_addr;
+#define SIN_ZERO_LEN 8
+ char sin_zero[SIN_ZERO_LEN];
+};
+#endif /* LWIP_IPV4 */
+
+#if LWIP_IPV6
+struct sockaddr_in6 {
+ u8_t sin6_len; /* length of this structure */
+ sa_family_t sin6_family; /* AF_INET6 */
+ in_port_t sin6_port; /* Transport layer port # */
+ u32_t sin6_flowinfo; /* IPv6 flow information */
+ struct in6_addr sin6_addr; /* IPv6 address */
+ u32_t sin6_scope_id; /* Set of interfaces for scope */
+};
+#endif /* LWIP_IPV6 */
+
+struct sockaddr {
+ u8_t sa_len;
+ sa_family_t sa_family;
+ char sa_data[14];
+};
+
+struct sockaddr_storage {
+ u8_t s2_len;
+ sa_family_t ss_family;
+ char s2_data1[2];
+ u32_t s2_data2[3];
+#if LWIP_IPV6
+ u32_t s2_data3[3];
+#endif /* LWIP_IPV6 */
+};
+
+/* 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
+
+struct lwip_sock;
+
+#if !LWIP_TCPIP_CORE_LOCKING
+/** Maximum optlen used by setsockopt/getsockopt */
+#define LWIP_SETGETSOCKOPT_MAXOPTLEN 16
+
+/** This struct is used to pass data to the set/getsockopt_internal
+ * functions running in tcpip_thread context (only a void* is allowed) */
+struct lwip_setgetsockopt_data {
+ /** socket index for which to change options */
+ int s;
+ /** level of the option to process */
+ int level;
+ /** name of the option to process */
+ int optname;
+ /** set: value to set the option to
+ * get: value of the option is stored here */
+#if LWIP_MPU_COMPATIBLE
+ u8_t optval[LWIP_SETGETSOCKOPT_MAXOPTLEN];
+#else
+ union {
+ void *p;
+ const void *pc;
+ } optval;
+#endif
+ /** size of *optval */
+ socklen_t optlen;
+ /** if an error occurs, it is temporarily stored here */
+ err_t err;
+ /** semaphore to wake up the calling task */
+ void* completed_sem;
+};
+#endif /* !LWIP_TCPIP_CORE_LOCKING */
+
+#if !defined(iovec)
+struct iovec {
+ void *iov_base;
+ size_t iov_len;
+};
+#endif
+
+struct msghdr {
+ void *msg_name;
+ socklen_t msg_namelen;
+ struct iovec *msg_iov;
+ int msg_iovlen;
+ void *msg_control;
+ socklen_t msg_controllen;
+ int msg_flags;
+};
+
+/* Socket protocol types (TCP/UDP/RAW) */
+#define SOCK_STREAM 1
+#define SOCK_DGRAM 2
+#define SOCK_RAW 3
+
+/*
+ * Option flags per-socket. These must match the SOF_ flags in ip.h (checked in init.c)
+ */
+#define SO_REUSEADDR 0x0004 /* Allow local address reuse */
+#define SO_KEEPALIVE 0x0008 /* keep connections alive */
+#define SO_BROADCAST 0x0020 /* permit to send and to receive broadcast messages (see IP_SOF_BROADCAST option) */
+
+
+/*
+ * Additional options, not kept in so_options.
+ */
+#define SO_DEBUG 0x0001 /* Unimplemented: turn on debugging info recording */
+#define SO_ACCEPTCONN 0x0002 /* socket has had listen() */
+#define SO_DONTROUTE 0x0010 /* Unimplemented: just use interface addresses */
+#define SO_USELOOPBACK 0x0040 /* Unimplemented: bypass hardware when possible */
+#define SO_LINGER 0x0080 /* linger on close if data present */
+#define SO_DONTLINGER ((int)(~SO_LINGER))
+#define SO_OOBINLINE 0x0100 /* Unimplemented: leave received OOB data in line */
+#define SO_REUSEPORT 0x0200 /* Unimplemented: allow local address & port reuse */
+#define SO_SNDBUF 0x1001 /* Unimplemented: send buffer size */
+#define SO_RCVBUF 0x1002 /* receive buffer size */
+#define SO_SNDLOWAT 0x1003 /* Unimplemented: send low-water mark */
+#define SO_RCVLOWAT 0x1004 /* Unimplemented: receive low-water mark */
+#define SO_SNDTIMEO 0x1005 /* send timeout */
+#define SO_RCVTIMEO 0x1006 /* receive timeout */
+#define SO_ERROR 0x1007 /* get error status and clear */
+#define SO_TYPE 0x1008 /* get socket type */
+#define SO_CONTIMEO 0x1009 /* Unimplemented: connect timeout */
+#define SO_NO_CHECK 0x100a /* don't create UDP checksum */
+
+
+/*
+ * Structure used for manipulating linger option.
+ */
+struct linger {
+ int l_onoff; /* option on/off */
+ int l_linger; /* linger time in seconds */
+};
+
+/*
+ * Level number for (get/set)sockopt() to apply to socket itself.
+ */
+#define SOL_SOCKET 0xfff /* options for socket level */
+
+
+#define AF_UNSPEC 0
+#define AF_INET 2
+#if LWIP_IPV6
+#define AF_INET6 10
+#else /* LWIP_IPV6 */
+#define AF_INET6 AF_UNSPEC
+#endif /* LWIP_IPV6 */
+#define PF_INET AF_INET
+#define PF_INET6 AF_INET6
+#define PF_UNSPEC AF_UNSPEC
+
+#define IPPROTO_IP 0
+#define IPPROTO_ICMP 1
+#define IPPROTO_TCP 6
+#define IPPROTO_UDP 17
+#if LWIP_IPV6
+#define IPPROTO_IPV6 41
+#define IPPROTO_ICMPV6 58
+#endif /* LWIP_IPV6 */
+#define IPPROTO_UDPLITE 136
+#define IPPROTO_RAW 255
+
+/* Flags we can use with send and recv. */
+#define MSG_PEEK 0x01 /* Peeks at an incoming message */
+#define MSG_WAITALL 0x02 /* Unimplemented: Requests that the function block until the full amount of data requested can be returned */
+#define MSG_OOB 0x04 /* Unimplemented: Requests out-of-band data. The significance and semantics of out-of-band data are protocol-specific */
+#define MSG_DONTWAIT 0x08 /* Nonblocking i/o for this operation only */
+#define MSG_MORE 0x10 /* Sender will send more */
+
+
+/*
+ * Options for level IPPROTO_IP
+ */
+#define IP_TOS 1
+#define IP_TTL 2
+
+#if LWIP_TCP
+/*
+ * Options for level IPPROTO_TCP
+ */
+#define TCP_NODELAY 0x01 /* don't delay send to coalesce packets */
+#define TCP_KEEPALIVE 0x02 /* send KEEPALIVE probes when idle for pcb->keep_idle milliseconds */
+#define TCP_KEEPIDLE 0x03 /* set pcb->keep_idle - Same as TCP_KEEPALIVE, but use seconds for get/setsockopt */
+#define TCP_KEEPINTVL 0x04 /* set pcb->keep_intvl - Use seconds for get/setsockopt */
+#define TCP_KEEPCNT 0x05 /* set pcb->keep_cnt - Use number of probes sent for get/setsockopt */
+#endif /* LWIP_TCP */
+
+#if LWIP_IPV6
+/*
+ * Options for level IPPROTO_IPV6
+ */
+#define IPV6_CHECKSUM 7 /* RFC3542: calculate and insert the ICMPv6 checksum for raw sockets. */
+#define IPV6_V6ONLY 27 /* RFC3493: boolean control to restrict AF_INET6 sockets to IPv6 communications only. */
+#endif /* LWIP_IPV6 */
+
+#if LWIP_UDP && LWIP_UDPLITE
+/*
+ * Options for level IPPROTO_UDPLITE
+ */
+#define UDPLITE_SEND_CSCOV 0x01 /* sender checksum coverage */
+#define UDPLITE_RECV_CSCOV 0x02 /* minimal receiver checksum coverage */
+#endif /* LWIP_UDP && LWIP_UDPLITE*/
+
+
+#if LWIP_MULTICAST_TX_OPTIONS
+/*
+ * Options and types for UDP multicast traffic handling
+ */
+#define IP_MULTICAST_TTL 5
+#define IP_MULTICAST_IF 6
+#define IP_MULTICAST_LOOP 7
+#endif /* LWIP_MULTICAST_TX_OPTIONS */
+
+#if LWIP_IGMP
+/*
+ * Options and types related to multicast membership
+ */
+#define IP_ADD_MEMBERSHIP 3
+#define IP_DROP_MEMBERSHIP 4
+
+typedef struct ip_mreq {
+ struct in_addr imr_multiaddr; /* IP multicast address of group */
+ struct in_addr imr_interface; /* local IP address of interface */
+} ip_mreq;
+#endif /* LWIP_IGMP */
+
+/*
+ * The Type of Service provides an indication of the abstract
+ * parameters of the quality of service desired. These parameters are
+ * to be used to guide the selection of the actual service parameters
+ * when transmitting a datagram through a particular network. Several
+ * networks offer service precedence, which somehow treats high
+ * precedence traffic as more important than other traffic (generally
+ * by accepting only traffic above a certain precedence at time of high
+ * load). The major choice is a three way tradeoff between low-delay,
+ * high-reliability, and high-throughput.
+ * The use of the Delay, Throughput, and Reliability indications may
+ * increase the cost (in some sense) of the service. In many networks
+ * better performance for one of these parameters is coupled with worse
+ * performance on another. Except for very unusual cases at most two
+ * of these three indications should be set.
+ */
+#define IPTOS_TOS_MASK 0x1E
+#define IPTOS_TOS(tos) ((tos) & IPTOS_TOS_MASK)
+#define IPTOS_LOWDELAY 0x10
+#define IPTOS_THROUGHPUT 0x08
+#define IPTOS_RELIABILITY 0x04
+#define IPTOS_LOWCOST 0x02
+#define IPTOS_MINCOST IPTOS_LOWCOST
+
+/*
+ * The Network Control precedence designation is intended to be used
+ * within a network only. The actual use and control of that
+ * designation is up to each network. The Internetwork Control
+ * designation is intended for use by gateway control originators only.
+ * If the actual use of these precedence designations is of concern to
+ * a particular network, it is the responsibility of that network to
+ * control the access to, and use of, those precedence designations.
+ */
+#define IPTOS_PREC_MASK 0xe0
+#define IPTOS_PREC(tos) ((tos) & IPTOS_PREC_MASK)
+#define IPTOS_PREC_NETCONTROL 0xe0
+#define IPTOS_PREC_INTERNETCONTROL 0xc0
+#define IPTOS_PREC_CRITIC_ECP 0xa0
+#define IPTOS_PREC_FLASHOVERRIDE 0x80
+#define IPTOS_PREC_FLASH 0x60
+#define IPTOS_PREC_IMMEDIATE 0x40
+#define IPTOS_PREC_PRIORITY 0x20
+#define IPTOS_PREC_ROUTINE 0x00
+
+
+/*
+ * Commands for ioctlsocket(), taken from the BSD file fcntl.h.
+ * lwip_ioctl only supports FIONREAD and FIONBIO, for now
+ *
+ * Ioctl's have the command encoded in the lower word,
+ * and the size of any in or out parameters in the upper
+ * word. The high 2 bits of the upper word are used
+ * to encode the in/out status of the parameter; for now
+ * we restrict parameters to at most 128 bytes.
+ */
+#if !defined(FIONREAD) || !defined(FIONBIO)
+#define IOCPARM_MASK 0x7fU /* parameters must be < 128 bytes */
+#define IOC_VOID 0x20000000UL /* no parameters */
+#define IOC_OUT 0x40000000UL /* copy out parameters */
+#define IOC_IN 0x80000000UL /* copy in parameters */
+#define IOC_INOUT (IOC_IN|IOC_OUT)
+ /* 0x20000000 distinguishes new &
+ old ioctl's */
+#define _IO(x,y) (IOC_VOID|((x)<<8)|(y))
+
+#define _IOR(x,y,t) (IOC_OUT|(((long)sizeof(t)&IOCPARM_MASK)<<16)|((x)<<8)|(y))
+
+#define _IOW(x,y,t) (IOC_IN|(((long)sizeof(t)&IOCPARM_MASK)<<16)|((x)<<8)|(y))
+#endif /* !defined(FIONREAD) || !defined(FIONBIO) */
+
+#ifndef FIONREAD
+#define FIONREAD _IOR('f', 127, unsigned long) /* get # bytes to read */
+#endif
+#ifndef FIONBIO
+#define FIONBIO _IOW('f', 126, unsigned long) /* set/clear non-blocking i/o */
+#endif
+
+/* Socket I/O Controls: unimplemented */
+#ifndef SIOCSHIWAT
+#define SIOCSHIWAT _IOW('s', 0, unsigned long) /* set high watermark */
+#define SIOCGHIWAT _IOR('s', 1, unsigned long) /* get high watermark */
+#define SIOCSLOWAT _IOW('s', 2, unsigned long) /* set low watermark */
+#define SIOCGLOWAT _IOR('s', 3, unsigned long) /* get low watermark */
+#define SIOCATMARK _IOR('s', 7, unsigned long) /* at oob mark? */
+#endif
+
+/* commands for fnctl */
+#ifndef F_GETFL
+#define F_GETFL 3
+#endif
+#ifndef F_SETFL
+#define F_SETFL 4
+#endif
+
+/* File status flags and file access modes for fnctl,
+ these are bits in an int. */
+#ifndef O_NONBLOCK
+#define O_NONBLOCK 1 /* nonblocking I/O */
+#endif
+#ifndef O_NDELAY
+#define O_NDELAY 1 /* same as O_NONBLOCK, for compatibility */
+#endif
+
+#ifndef SHUT_RD
+ #define SHUT_RD 0
+ #define SHUT_WR 1
+ #define SHUT_RDWR 2
+#endif
+
+/* FD_SET used for lwip_select */
+#ifndef FD_SET
+#undef FD_SETSIZE
+/* Make FD_SETSIZE match NUM_SOCKETS in socket.c */
+#define FD_SETSIZE MEMP_NUM_NETCONN
+#define FDSETSAFESET(n, code) do { \
+ if (((n) - LWIP_SOCKET_OFFSET < MEMP_NUM_NETCONN) && (((int)(n) - LWIP_SOCKET_OFFSET) >= 0)) { \
+ code; }} while(0)
+#define FDSETSAFEGET(n, code) (((n) - LWIP_SOCKET_OFFSET < MEMP_NUM_NETCONN) && (((int)(n) - LWIP_SOCKET_OFFSET) >= 0) ?\
+ (code) : 0)
+#define FD_SET(n, p) FDSETSAFESET(n, (p)->fd_bits[((n)-LWIP_SOCKET_OFFSET)/8] |= (1 << (((n)-LWIP_SOCKET_OFFSET) & 7)))
+#define FD_CLR(n, p) FDSETSAFESET(n, (p)->fd_bits[((n)-LWIP_SOCKET_OFFSET)/8] &= ~(1 << (((n)-LWIP_SOCKET_OFFSET) & 7)))
+#define FD_ISSET(n,p) FDSETSAFEGET(n, (p)->fd_bits[((n)-LWIP_SOCKET_OFFSET)/8] & (1 << (((n)-LWIP_SOCKET_OFFSET) & 7)))
+#define FD_ZERO(p) memset((void*)(p), 0, sizeof(*(p)))
+
+typedef struct fd_set
+{
+ unsigned char fd_bits [(FD_SETSIZE+7)/8];
+} fd_set;
+
+#elif LWIP_SOCKET_OFFSET
+#error LWIP_SOCKET_OFFSET does not work with external FD_SET!
+#elif FD_SETSIZE < MEMP_NUM_NETCONN
+#error "external FD_SETSIZE too small for number of sockets"
+#endif /* FD_SET */
+
+/** LWIP_TIMEVAL_PRIVATE: if you want to use the struct timeval provided
+ * by your system, set this to 0 and include <sys/time.h> in cc.h */
+#ifndef LWIP_TIMEVAL_PRIVATE
+#define LWIP_TIMEVAL_PRIVATE 1
+#endif
+
+#if LWIP_TIMEVAL_PRIVATE
+struct timeval {
+ long tv_sec; /* seconds */
+ long tv_usec; /* and microseconds */
+};
+#endif /* LWIP_TIMEVAL_PRIVATE */
+
+#define lwip_socket_init() /* Compatibility define, no init needed. */
+void lwip_socket_thread_init(void); /* LWIP_NETCONN_SEM_PER_THREAD==1: initialize thread-local semaphore */
+void lwip_socket_thread_cleanup(void); /* LWIP_NETCONN_SEM_PER_THREAD==1: destroy thread-local semaphore */
+
+#if LWIP_COMPAT_SOCKETS == 2
+/* This helps code parsers/code completion by not having the COMPAT functions as defines */
+#define lwip_accept accept
+#define lwip_bind bind
+#define lwip_shutdown shutdown
+#define lwip_getpeername getpeername
+#define lwip_getsockname getsockname
+#define lwip_setsockopt setsockopt
+#define lwip_getsockopt getsockopt
+#define lwip_close closesocket
+#define lwip_connect connect
+#define lwip_listen listen
+#define lwip_recv recv
+#define lwip_recvfrom recvfrom
+#define lwip_send send
+#define lwip_sendmsg sendmsg
+#define lwip_sendto sendto
+#define lwip_socket socket
+#define lwip_select select
+#define lwip_ioctlsocket ioctl
+
+#if LWIP_POSIX_SOCKETS_IO_NAMES
+#define lwip_read read
+#define lwip_write write
+#define lwip_writev writev
+#undef lwip_close
+#define lwip_close close
+#define closesocket(s) close(s)
+#define lwip_fcntl fcntl
+#define lwip_ioctl ioctl
+#endif /* LWIP_POSIX_SOCKETS_IO_NAMES */
+#endif /* LWIP_COMPAT_SOCKETS == 2 */
+
+int lwip_accept(int s, struct sockaddr *addr, socklen_t *addrlen);
+int lwip_bind(int s, const struct sockaddr *name, socklen_t namelen);
+int lwip_shutdown(int s, int how);
+int lwip_getpeername (int s, struct sockaddr *name, socklen_t *namelen);
+int lwip_getsockname (int s, struct sockaddr *name, socklen_t *namelen);
+int lwip_getsockopt (int s, int level, int optname, void *optval, socklen_t *optlen);
+int lwip_setsockopt (int s, int level, int optname, const void *optval, socklen_t optlen);
+int lwip_close(int s);
+int lwip_connect(int s, const struct sockaddr *name, socklen_t namelen);
+int lwip_listen(int s, int backlog);
+int lwip_recv(int s, void *mem, size_t len, int flags);
+int lwip_read(int s, void *mem, size_t len);
+int lwip_recvfrom(int s, void *mem, size_t len, int flags,
+ struct sockaddr *from, socklen_t *fromlen);
+int lwip_send(int s, const void *dataptr, size_t size, int flags);
+int lwip_sendmsg(int s, const struct msghdr *message, int flags);
+int lwip_sendto(int s, const void *dataptr, size_t size, int flags,
+ const struct sockaddr *to, socklen_t tolen);
+int lwip_socket(int domain, int type, int protocol);
+int lwip_write(int s, const void *dataptr, size_t size);
+int lwip_writev(int s, const struct iovec *iov, int iovcnt);
+int lwip_select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset,
+ struct timeval *timeout);
+int lwip_ioctl(int s, long cmd, void *argp);
+int lwip_fcntl(int s, int cmd, int val);
+
+#if LWIP_COMPAT_SOCKETS
+#if LWIP_COMPAT_SOCKETS != 2
+/** @ingroup socket */
+#define accept(s,addr,addrlen) lwip_accept(s,addr,addrlen)
+/** @ingroup socket */
+#define bind(s,name,namelen) lwip_bind(s,name,namelen)
+/** @ingroup socket */
+#define shutdown(s,how) lwip_shutdown(s,how)
+/** @ingroup socket */
+#define getpeername(s,name,namelen) lwip_getpeername(s,name,namelen)
+/** @ingroup socket */
+#define getsockname(s,name,namelen) lwip_getsockname(s,name,namelen)
+/** @ingroup socket */
+#define setsockopt(s,level,optname,opval,optlen) lwip_setsockopt(s,level,optname,opval,optlen)
+/** @ingroup socket */
+#define getsockopt(s,level,optname,opval,optlen) lwip_getsockopt(s,level,optname,opval,optlen)
+/** @ingroup socket */
+#define closesocket(s) lwip_close(s)
+/** @ingroup socket */
+#define connect(s,name,namelen) lwip_connect(s,name,namelen)
+/** @ingroup socket */
+#define listen(s,backlog) lwip_listen(s,backlog)
+/** @ingroup socket */
+#define recv(s,mem,len,flags) lwip_recv(s,mem,len,flags)
+/** @ingroup socket */
+#define recvfrom(s,mem,len,flags,from,fromlen) lwip_recvfrom(s,mem,len,flags,from,fromlen)
+/** @ingroup socket */
+#define send(s,dataptr,size,flags) lwip_send(s,dataptr,size,flags)
+/** @ingroup socket */
+#define sendmsg(s,message,flags) lwip_sendmsg(s,message,flags)
+/** @ingroup socket */
+#define sendto(s,dataptr,size,flags,to,tolen) lwip_sendto(s,dataptr,size,flags,to,tolen)
+/** @ingroup socket */
+#define socket(domain,type,protocol) lwip_socket(domain,type,protocol)
+/** @ingroup socket */
+#define select(maxfdp1,readset,writeset,exceptset,timeout) lwip_select(maxfdp1,readset,writeset,exceptset,timeout)
+/** @ingroup socket */
+#define ioctlsocket(s,cmd,argp) lwip_ioctl(s,cmd,argp)
+
+#if LWIP_POSIX_SOCKETS_IO_NAMES
+/** @ingroup socket */
+#define read(s,mem,len) lwip_read(s,mem,len)
+/** @ingroup socket */
+#define write(s,dataptr,len) lwip_write(s,dataptr,len)
+/** @ingroup socket */
+#define writev(s,iov,iovcnt) lwip_writev(s,iov,iovcnt)
+/** @ingroup socket */
+#define close(s) lwip_close(s)
+/** @ingroup socket */
+#define fcntl(s,cmd,val) lwip_fcntl(s,cmd,val)
+/** @ingroup socket */
+#define ioctl(s,cmd,argp) lwip_ioctl(s,cmd,argp)
+#endif /* LWIP_POSIX_SOCKETS_IO_NAMES */
+#endif /* LWIP_COMPAT_SOCKETS != 2 */
+
+#if LWIP_IPV4 && LWIP_IPV6
+/** @ingroup socket */
+#define inet_ntop(af,src,dst,size) \
+ (((af) == AF_INET6) ? ip6addr_ntoa_r((const ip6_addr_t*)(src),(dst),(size)) \
+ : (((af) == AF_INET) ? ip4addr_ntoa_r((const ip4_addr_t*)(src),(dst),(size)) : NULL))
+/** @ingroup socket */
+#define inet_pton(af,src,dst) \
+ (((af) == AF_INET6) ? ip6addr_aton((src),(ip6_addr_t*)(dst)) \
+ : (((af) == AF_INET) ? ip4addr_aton((src),(ip4_addr_t*)(dst)) : 0))
+#elif LWIP_IPV4 /* LWIP_IPV4 && LWIP_IPV6 */
+#define inet_ntop(af,src,dst,size) \
+ (((af) == AF_INET) ? ip4addr_ntoa_r((const ip4_addr_t*)(src),(dst),(size)) : NULL)
+#define inet_pton(af,src,dst) \
+ (((af) == AF_INET) ? ip4addr_aton((src),(ip4_addr_t*)(dst)) : 0)
+#else /* LWIP_IPV4 && LWIP_IPV6 */
+#define inet_ntop(af,src,dst,size) \
+ (((af) == AF_INET6) ? ip6addr_ntoa_r((const ip6_addr_t*)(src),(dst),(size)) : NULL)
+#define inet_pton(af,src,dst) \
+ (((af) == AF_INET6) ? ip6addr_aton((src),(ip6_addr_t*)(dst)) : 0)
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+
+#endif /* LWIP_COMPAT_SOCKETS */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_SOCKET */
+
+#endif /* LWIP_HDR_SOCKETS_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/stats.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/stats.h
new file mode 100644
index 0000000..bcda2ac
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/stats.h
@@ -0,0 +1,491 @@
+/**
+ * @file
+ * Statistics API (to be used from TCPIP thread)
+ */
+
+/*
+ * 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_HDR_STATS_H
+#define LWIP_HDR_STATS_H
+
+#include "lwip/opt.h"
+
+#include "lwip/mem.h"
+#include "lwip/memp.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if LWIP_STATS
+
+#ifndef LWIP_STATS_LARGE
+#define LWIP_STATS_LARGE 0
+#endif
+
+#if LWIP_STATS_LARGE
+#define STAT_COUNTER u32_t
+#define STAT_COUNTER_F U32_F
+#else
+#define STAT_COUNTER u16_t
+#define STAT_COUNTER_F U16_F
+#endif
+
+/** Protocol related stats */
+struct stats_proto {
+ STAT_COUNTER xmit; /* Transmitted packets. */
+ STAT_COUNTER recv; /* Received packets. */
+ STAT_COUNTER fw; /* Forwarded packets. */
+ STAT_COUNTER drop; /* Dropped packets. */
+ STAT_COUNTER chkerr; /* Checksum error. */
+ STAT_COUNTER lenerr; /* Invalid length error. */
+ STAT_COUNTER memerr; /* Out of memory error. */
+ STAT_COUNTER rterr; /* Routing error. */
+ STAT_COUNTER proterr; /* Protocol error. */
+ STAT_COUNTER opterr; /* Error in options. */
+ STAT_COUNTER err; /* Misc error. */
+ STAT_COUNTER cachehit;
+};
+
+/** IGMP stats */
+struct stats_igmp {
+ STAT_COUNTER xmit; /* Transmitted packets. */
+ STAT_COUNTER recv; /* Received packets. */
+ STAT_COUNTER drop; /* Dropped packets. */
+ STAT_COUNTER chkerr; /* Checksum error. */
+ STAT_COUNTER lenerr; /* Invalid length error. */
+ STAT_COUNTER memerr; /* Out of memory error. */
+ STAT_COUNTER proterr; /* Protocol error. */
+ STAT_COUNTER rx_v1; /* Received v1 frames. */
+ STAT_COUNTER rx_group; /* Received group-specific queries. */
+ STAT_COUNTER rx_general; /* Received general queries. */
+ STAT_COUNTER rx_report; /* Received reports. */
+ STAT_COUNTER tx_join; /* Sent joins. */
+ STAT_COUNTER tx_leave; /* Sent leaves. */
+ STAT_COUNTER tx_report; /* Sent reports. */
+};
+
+/** Memory stats */
+struct stats_mem {
+#if defined(LWIP_DEBUG) || LWIP_STATS_DISPLAY
+ const char *name;
+#endif /* defined(LWIP_DEBUG) || LWIP_STATS_DISPLAY */
+ STAT_COUNTER err;
+ mem_size_t avail;
+ mem_size_t used;
+ mem_size_t max;
+ STAT_COUNTER illegal;
+};
+
+/** System element stats */
+struct stats_syselem {
+ STAT_COUNTER used;
+ STAT_COUNTER max;
+ STAT_COUNTER err;
+};
+
+/** System stats */
+struct stats_sys {
+ struct stats_syselem sem;
+ struct stats_syselem mutex;
+ struct stats_syselem mbox;
+};
+
+/** SNMP MIB2 stats */
+struct stats_mib2 {
+ /* IP */
+ u32_t ipinhdrerrors;
+ u32_t ipinaddrerrors;
+ u32_t ipinunknownprotos;
+ u32_t ipindiscards;
+ u32_t ipindelivers;
+ u32_t ipoutrequests;
+ u32_t ipoutdiscards;
+ u32_t ipoutnoroutes;
+ u32_t ipreasmoks;
+ u32_t ipreasmfails;
+ u32_t ipfragoks;
+ u32_t ipfragfails;
+ u32_t ipfragcreates;
+ u32_t ipreasmreqds;
+ u32_t ipforwdatagrams;
+ u32_t ipinreceives;
+
+ /* TCP */
+ u32_t tcpactiveopens;
+ u32_t tcppassiveopens;
+ u32_t tcpattemptfails;
+ u32_t tcpestabresets;
+ u32_t tcpoutsegs;
+ u32_t tcpretranssegs;
+ u32_t tcpinsegs;
+ u32_t tcpinerrs;
+ u32_t tcpoutrsts;
+
+ /* UDP */
+ u32_t udpindatagrams;
+ u32_t udpnoports;
+ u32_t udpinerrors;
+ u32_t udpoutdatagrams;
+
+ /* ICMP */
+ u32_t icmpinmsgs;
+ u32_t icmpinerrors;
+ u32_t icmpindestunreachs;
+ u32_t icmpintimeexcds;
+ u32_t icmpinparmprobs;
+ u32_t icmpinsrcquenchs;
+ u32_t icmpinredirects;
+ u32_t icmpinechos;
+ u32_t icmpinechoreps;
+ u32_t icmpintimestamps;
+ u32_t icmpintimestampreps;
+ u32_t icmpinaddrmasks;
+ u32_t icmpinaddrmaskreps;
+ u32_t icmpoutmsgs;
+ u32_t icmpouterrors;
+ u32_t icmpoutdestunreachs;
+ u32_t icmpouttimeexcds;
+ u32_t icmpoutechos; /* can be incremented by user application ('ping') */
+ u32_t icmpoutechoreps;
+};
+
+/**
+ * @ingroup netif_mib2
+ * SNMP MIB2 interface stats
+ */
+struct stats_mib2_netif_ctrs {
+ /** The total number of octets received on the interface, including framing characters */
+ u32_t ifinoctets;
+ /** The number of packets, delivered by this sub-layer to a higher (sub-)layer, which were
+ * not addressed to a multicast or broadcast address at this sub-layer */
+ u32_t ifinucastpkts;
+ /** The number of packets, delivered by this sub-layer to a higher (sub-)layer, which were
+ * addressed to a multicast or broadcast address at this sub-layer */
+ u32_t ifinnucastpkts;
+ /** The number of inbound packets which were chosen to be discarded even though no errors had
+ * been detected to prevent their being deliverable to a higher-layer protocol. One possible
+ * reason for discarding such a packet could be to free up buffer space */
+ u32_t ifindiscards;
+ /** For packet-oriented interfaces, the number of inbound packets that contained errors
+ * preventing them from being deliverable to a higher-layer protocol. For character-
+ * oriented or fixed-length interfaces, the number of inbound transmission units that
+ * contained errors preventing them from being deliverable to a higher-layer protocol. */
+ u32_t ifinerrors;
+ /** For packet-oriented interfaces, the number of packets received via the interface which
+ * were discarded because of an unknown or unsupported protocol. For character-oriented
+ * or fixed-length interfaces that support protocol multiplexing the number of transmission
+ * units received via the interface which were discarded because of an unknown or unsupported
+ * protocol. For any interface that does not support protocol multiplexing, this counter will
+ * always be 0 */
+ u32_t ifinunknownprotos;
+ /** The total number of octets transmitted out of the interface, including framing characters. */
+ u32_t ifoutoctets;
+ /** The total number of packets that higher-level protocols requested be transmitted, and
+ * which were not addressed to a multicast or broadcast address at this sub-layer, including
+ * those that were discarded or not sent. */
+ u32_t ifoutucastpkts;
+ /** The total number of packets that higher-level protocols requested be transmitted, and which
+ * were addressed to a multicast or broadcast address at this sub-layer, including
+ * those that were discarded or not sent. */
+ u32_t ifoutnucastpkts;
+ /** The number of outbound packets which were chosen to be discarded even though no errors had
+ * been detected to prevent their being transmitted. One possible reason for discarding
+ * such a packet could be to free up buffer space. */
+ u32_t ifoutdiscards;
+ /** For packet-oriented interfaces, the number of outbound packets that could not be transmitted
+ * because of errors. For character-oriented or fixed-length interfaces, the number of outbound
+ * transmission units that could not be transmitted because of errors. */
+ u32_t ifouterrors;
+};
+
+/** lwIP stats container */
+struct stats_ {
+#if LINK_STATS
+ /** Link level */
+ struct stats_proto link;
+#endif
+#if ETHARP_STATS
+ /** ARP */
+ struct stats_proto etharp;
+#endif
+#if IPFRAG_STATS
+ /** Fragmentation */
+ struct stats_proto ip_frag;
+#endif
+#if IP_STATS
+ /** IP */
+ struct stats_proto ip;
+#endif
+#if ICMP_STATS
+ /** ICMP */
+ struct stats_proto icmp;
+#endif
+#if IGMP_STATS
+ /** IGMP */
+ struct stats_igmp igmp;
+#endif
+#if UDP_STATS
+ /** UDP */
+ struct stats_proto udp;
+#endif
+#if TCP_STATS
+ /** TCP */
+ struct stats_proto tcp;
+#endif
+#if MEM_STATS
+ /** Heap */
+ struct stats_mem mem;
+#endif
+#if MEMP_STATS
+ /** Internal memory pools */
+ struct stats_mem *memp[MEMP_MAX];
+#endif
+#if SYS_STATS
+ /** System */
+ struct stats_sys sys;
+#endif
+#if IP6_STATS
+ /** IPv6 */
+ struct stats_proto ip6;
+#endif
+#if ICMP6_STATS
+ /** ICMP6 */
+ struct stats_proto icmp6;
+#endif
+#if IP6_FRAG_STATS
+ /** IPv6 fragmentation */
+ struct stats_proto ip6_frag;
+#endif
+#if MLD6_STATS
+ /** Multicast listener discovery */
+ struct stats_igmp mld6;
+#endif
+#if ND6_STATS
+ /** Neighbor discovery */
+ struct stats_proto nd6;
+#endif
+#if MIB2_STATS
+ /** SNMP MIB2 */
+ struct stats_mib2 mib2;
+#endif
+};
+
+/** Global variable containing lwIP internal statistics. Add this to your debugger's watchlist. */
+extern struct stats_ lwip_stats;
+
+/** Init statistics */
+void stats_init(void);
+
+#define STATS_INC(x) ++lwip_stats.x
+#define STATS_DEC(x) --lwip_stats.x
+#define STATS_INC_USED(x, y) do { lwip_stats.x.used += y; \
+ if (lwip_stats.x.max < lwip_stats.x.used) { \
+ lwip_stats.x.max = lwip_stats.x.used; \
+ } \
+ } while(0)
+#define STATS_GET(x) lwip_stats.x
+#else /* LWIP_STATS */
+#define stats_init()
+#define STATS_INC(x)
+#define STATS_DEC(x)
+#define STATS_INC_USED(x)
+#endif /* LWIP_STATS */
+
+#if TCP_STATS
+#define TCP_STATS_INC(x) STATS_INC(x)
+#define TCP_STATS_DISPLAY() stats_display_proto(&lwip_stats.tcp, "TCP")
+#else
+#define TCP_STATS_INC(x)
+#define TCP_STATS_DISPLAY()
+#endif
+
+#if UDP_STATS
+#define UDP_STATS_INC(x) STATS_INC(x)
+#define UDP_STATS_DISPLAY() stats_display_proto(&lwip_stats.udp, "UDP")
+#else
+#define UDP_STATS_INC(x)
+#define UDP_STATS_DISPLAY()
+#endif
+
+#if ICMP_STATS
+#define ICMP_STATS_INC(x) STATS_INC(x)
+#define ICMP_STATS_DISPLAY() stats_display_proto(&lwip_stats.icmp, "ICMP")
+#else
+#define ICMP_STATS_INC(x)
+#define ICMP_STATS_DISPLAY()
+#endif
+
+#if IGMP_STATS
+#define IGMP_STATS_INC(x) STATS_INC(x)
+#define IGMP_STATS_DISPLAY() stats_display_igmp(&lwip_stats.igmp, "IGMP")
+#else
+#define IGMP_STATS_INC(x)
+#define IGMP_STATS_DISPLAY()
+#endif
+
+#if IP_STATS
+#define IP_STATS_INC(x) STATS_INC(x)
+#define IP_STATS_DISPLAY() stats_display_proto(&lwip_stats.ip, "IP")
+#else
+#define IP_STATS_INC(x)
+#define IP_STATS_DISPLAY()
+#endif
+
+#if IPFRAG_STATS
+#define IPFRAG_STATS_INC(x) STATS_INC(x)
+#define IPFRAG_STATS_DISPLAY() stats_display_proto(&lwip_stats.ip_frag, "IP_FRAG")
+#else
+#define IPFRAG_STATS_INC(x)
+#define IPFRAG_STATS_DISPLAY()
+#endif
+
+#if ETHARP_STATS
+#define ETHARP_STATS_INC(x) STATS_INC(x)
+#define ETHARP_STATS_DISPLAY() stats_display_proto(&lwip_stats.etharp, "ETHARP")
+#else
+#define ETHARP_STATS_INC(x)
+#define ETHARP_STATS_DISPLAY()
+#endif
+
+#if LINK_STATS
+#define LINK_STATS_INC(x) STATS_INC(x)
+#define LINK_STATS_DISPLAY() stats_display_proto(&lwip_stats.link, "LINK")
+#else
+#define LINK_STATS_INC(x)
+#define LINK_STATS_DISPLAY()
+#endif
+
+#if MEM_STATS
+#define MEM_STATS_AVAIL(x, y) lwip_stats.mem.x = y
+#define MEM_STATS_INC(x) STATS_INC(mem.x)
+#define MEM_STATS_INC_USED(x, y) STATS_INC_USED(mem, y)
+#define MEM_STATS_DEC_USED(x, y) lwip_stats.mem.x -= y
+#define MEM_STATS_DISPLAY() stats_display_mem(&lwip_stats.mem, "HEAP")
+#else
+#define MEM_STATS_AVAIL(x, y)
+#define MEM_STATS_INC(x)
+#define MEM_STATS_INC_USED(x, y)
+#define MEM_STATS_DEC_USED(x, y)
+#define MEM_STATS_DISPLAY()
+#endif
+
+ #if MEMP_STATS
+#define MEMP_STATS_DEC(x, i) STATS_DEC(memp[i]->x)
+#define MEMP_STATS_DISPLAY(i) stats_display_memp(lwip_stats.memp[i], i)
+#define MEMP_STATS_GET(x, i) STATS_GET(memp[i]->x)
+ #else
+#define MEMP_STATS_DEC(x, i)
+#define MEMP_STATS_DISPLAY(i)
+#define MEMP_STATS_GET(x, i) 0
+#endif
+
+#if SYS_STATS
+#define SYS_STATS_INC(x) STATS_INC(sys.x)
+#define SYS_STATS_DEC(x) STATS_DEC(sys.x)
+#define SYS_STATS_INC_USED(x) STATS_INC_USED(sys.x, 1)
+#define SYS_STATS_DISPLAY() stats_display_sys(&lwip_stats.sys)
+#else
+#define SYS_STATS_INC(x)
+#define SYS_STATS_DEC(x)
+#define SYS_STATS_INC_USED(x)
+#define SYS_STATS_DISPLAY()
+#endif
+
+#if IP6_STATS
+#define IP6_STATS_INC(x) STATS_INC(x)
+#define IP6_STATS_DISPLAY() stats_display_proto(&lwip_stats.ip6, "IPv6")
+#else
+#define IP6_STATS_INC(x)
+#define IP6_STATS_DISPLAY()
+#endif
+
+#if ICMP6_STATS
+#define ICMP6_STATS_INC(x) STATS_INC(x)
+#define ICMP6_STATS_DISPLAY() stats_display_proto(&lwip_stats.icmp6, "ICMPv6")
+#else
+#define ICMP6_STATS_INC(x)
+#define ICMP6_STATS_DISPLAY()
+#endif
+
+#if IP6_FRAG_STATS
+#define IP6_FRAG_STATS_INC(x) STATS_INC(x)
+#define IP6_FRAG_STATS_DISPLAY() stats_display_proto(&lwip_stats.ip6_frag, "IPv6 FRAG")
+#else
+#define IP6_FRAG_STATS_INC(x)
+#define IP6_FRAG_STATS_DISPLAY()
+#endif
+
+#if MLD6_STATS
+#define MLD6_STATS_INC(x) STATS_INC(x)
+#define MLD6_STATS_DISPLAY() stats_display_igmp(&lwip_stats.mld6, "MLDv1")
+#else
+#define MLD6_STATS_INC(x)
+#define MLD6_STATS_DISPLAY()
+#endif
+
+#if ND6_STATS
+#define ND6_STATS_INC(x) STATS_INC(x)
+#define ND6_STATS_DISPLAY() stats_display_proto(&lwip_stats.nd6, "ND")
+#else
+#define ND6_STATS_INC(x)
+#define ND6_STATS_DISPLAY()
+#endif
+
+#if MIB2_STATS
+#define MIB2_STATS_INC(x) STATS_INC(x)
+#else
+#define MIB2_STATS_INC(x)
+#endif
+
+/* Display of statistics */
+#if LWIP_STATS_DISPLAY
+void stats_display(void);
+void stats_display_proto(struct stats_proto *proto, const char *name);
+void stats_display_igmp(struct stats_igmp *igmp, const 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 */
+#define stats_display()
+#define stats_display_proto(proto, name)
+#define stats_display_igmp(igmp, name)
+#define stats_display_mem(mem, name)
+#define stats_display_memp(mem, index)
+#define stats_display_sys(sys)
+#endif /* LWIP_STATS_DISPLAY */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_STATS_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/sys.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/sys.h
new file mode 100644
index 0000000..d12bae0
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/sys.h
@@ -0,0 +1,455 @@
+/**
+ * @file
+ * OS abstraction layer
+ */
+
+/*
+ * 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_HDR_SYS_H
+#define LWIP_HDR_SYS_H
+
+#include "lwip/opt.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if NO_SYS
+
+/* For a totally minimal and standalone system, we provide null
+ definitions of the sys_ functions. */
+typedef u8_t sys_sem_t;
+typedef u8_t sys_mutex_t;
+typedef u8_t sys_mbox_t;
+
+#define sys_sem_new(s, c) ERR_OK
+#define sys_sem_signal(s)
+#define sys_sem_wait(s)
+#define sys_arch_sem_wait(s,t)
+#define sys_sem_free(s)
+#define sys_sem_valid(s) 0
+#define sys_sem_valid_val(s) 0
+#define sys_sem_set_invalid(s)
+#define sys_sem_set_invalid_val(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_valid_val(m)
+#define sys_mbox_set_invalid(m)
+#define sys_mbox_set_invalid_val(m)
+
+#define sys_thread_new(n,t,a,s,p)
+
+#define sys_msleep(t)
+
+#else /* NO_SYS */
+
+/** Return code for timeouts from sys_arch_mbox_fetch and sys_arch_sem_wait */
+#define SYS_ARCH_TIMEOUT 0xffffffffUL
+
+/** sys_mbox_tryfetch() returns SYS_MBOX_EMPTY if appropriate.
+ * For now we use the same magic value, but we allow this to change in future.
+ */
+#define SYS_MBOX_EMPTY SYS_ARCH_TIMEOUT
+
+#include "lwip/err.h"
+#include "arch/sys_arch.h"
+
+/** Function prototype for thread functions */
+typedef void (*lwip_thread_fn)(void *arg);
+
+/* Function prototypes for functions to be implemented by platform ports
+ (in sys_arch.c) */
+
+/* Mutex functions: */
+
+/** Define LWIP_COMPAT_MUTEX if the port has no mutexes and binary semaphores
+ should be used instead */
+#ifndef LWIP_COMPAT_MUTEX
+#define LWIP_COMPAT_MUTEX 0
+#endif
+
+#if LWIP_COMPAT_MUTEX
+/* for old ports that don't have mutexes: define them to binary semaphores */
+#define sys_mutex_t sys_sem_t
+#define sys_mutex_new(mutex) sys_sem_new(mutex, 1)
+#define sys_mutex_lock(mutex) sys_sem_wait(mutex)
+#define sys_mutex_unlock(mutex) sys_sem_signal(mutex)
+#define sys_mutex_free(mutex) sys_sem_free(mutex)
+#define sys_mutex_valid(mutex) sys_sem_valid(mutex)
+#define sys_mutex_set_invalid(mutex) sys_sem_set_invalid(mutex)
+
+#else /* LWIP_COMPAT_MUTEX */
+
+/**
+ * @ingroup sys_mutex
+ * Create a new mutex.
+ * Note that mutexes are expected to not be taken recursively by the lwIP code,
+ * so both implementation types (recursive or non-recursive) should work.
+ * @param mutex pointer to the mutex to create
+ * @return ERR_OK if successful, another err_t otherwise
+ */
+err_t sys_mutex_new(sys_mutex_t *mutex);
+/**
+ * @ingroup sys_mutex
+ * Lock a mutex
+ * @param mutex the mutex to lock
+ */
+void sys_mutex_lock(sys_mutex_t *mutex);
+/**
+ * @ingroup sys_mutex
+ * Unlock a mutex
+ * @param mutex the mutex to unlock
+ */
+void sys_mutex_unlock(sys_mutex_t *mutex);
+/**
+ * @ingroup sys_mutex
+ * Delete a semaphore
+ * @param mutex the mutex to delete
+ */
+void sys_mutex_free(sys_mutex_t *mutex);
+#ifndef sys_mutex_valid
+/**
+ * @ingroup sys_mutex
+ * Check if a mutex is valid/allocated: return 1 for valid, 0 for invalid
+ */
+int sys_mutex_valid(sys_mutex_t *mutex);
+#endif
+#ifndef sys_mutex_set_invalid
+/**
+ * @ingroup sys_mutex
+ * Set a mutex invalid so that sys_mutex_valid returns 0
+ */
+void sys_mutex_set_invalid(sys_mutex_t *mutex);
+#endif
+#endif /* LWIP_COMPAT_MUTEX */
+
+/* Semaphore functions: */
+
+/**
+ * @ingroup sys_sem
+ * Create a new semaphore
+ * @param sem pointer to the semaphore to create
+ * @param count initial count of the semaphore
+ * @return ERR_OK if successful, another err_t otherwise
+ */
+err_t sys_sem_new(sys_sem_t *sem, u8_t count);
+/**
+ * @ingroup sys_sem
+ * Signals a semaphore
+ * @param sem the semaphore to signal
+ */
+void sys_sem_signal(sys_sem_t *sem);
+/**
+ * @ingroup sys_sem
+ * Wait for a semaphore for the specified timeout
+ * @param sem the semaphore to wait for
+ * @param timeout timeout in milliseconds to wait (0 = wait forever)
+ * @return time (in milliseconds) waited for the semaphore
+ * or SYS_ARCH_TIMEOUT on timeout
+ */
+u32_t sys_arch_sem_wait(sys_sem_t *sem, u32_t timeout);
+/**
+ * @ingroup sys_sem
+ * Delete a semaphore
+ * @param sem semaphore to delete
+ */
+void sys_sem_free(sys_sem_t *sem);
+/** Wait for a semaphore - forever/no timeout */
+#define sys_sem_wait(sem) sys_arch_sem_wait(sem, 0)
+#ifndef sys_sem_valid
+/**
+ * @ingroup sys_sem
+ * Check if a semaphore is valid/allocated: return 1 for valid, 0 for invalid
+ */
+int sys_sem_valid(sys_sem_t *sem);
+#endif
+#ifndef sys_sem_set_invalid
+/**
+ * @ingroup sys_sem
+ * Set a semaphore invalid so that sys_sem_valid returns 0
+ */
+void sys_sem_set_invalid(sys_sem_t *sem);
+#endif
+#ifndef sys_sem_valid_val
+/**
+ * Same as sys_sem_valid() but taking a value, not a pointer
+ */
+#define sys_sem_valid_val(sem) sys_sem_valid(&(sem))
+#endif
+#ifndef sys_sem_set_invalid_val
+/**
+ * Same as sys_sem_set_invalid() but taking a value, not a pointer
+ */
+#define sys_sem_set_invalid_val(sem) sys_sem_set_invalid(&(sem))
+#endif
+
+#ifndef sys_msleep
+/**
+ * @ingroup sys_misc
+ * Sleep for specified number of ms
+ */
+void sys_msleep(u32_t ms); /* only has a (close to) 1 ms resolution. */
+#endif
+
+/* Mailbox functions. */
+
+/**
+ * @ingroup sys_mbox
+ * Create a new mbox of specified size
+ * @param mbox pointer to the mbox to create
+ * @param size (minimum) number of messages in this mbox
+ * @return ERR_OK if successful, another err_t otherwise
+ */
+err_t sys_mbox_new(sys_mbox_t *mbox, int size);
+/**
+ * @ingroup sys_mbox
+ * Post a message to an mbox - may not fail
+ * -> blocks if full, only used from tasks not from ISR
+ * @param mbox mbox to posts the message
+ * @param msg message to post (ATTENTION: can be NULL)
+ */
+void sys_mbox_post(sys_mbox_t *mbox, void *msg);
+/**
+ * @ingroup sys_mbox
+ * Try to post a message to an mbox - may fail if full or ISR
+ * @param mbox mbox to posts the message
+ * @param msg message to post (ATTENTION: can be NULL)
+ */
+err_t sys_mbox_trypost(sys_mbox_t *mbox, void *msg);
+/**
+ * @ingroup sys_mbox
+ * Wait for a new message to arrive in the mbox
+ * @param mbox mbox to get a message from
+ * @param msg pointer where the message is stored
+ * @param timeout maximum time (in milliseconds) to wait for a message (0 = wait forever)
+ * @return time (in milliseconds) waited for a message, may be 0 if not waited
+ or SYS_ARCH_TIMEOUT on timeout
+ * The returned time has to be accurate to prevent timer jitter!
+ */
+u32_t sys_arch_mbox_fetch(sys_mbox_t *mbox, void **msg, u32_t timeout);
+/* Allow port to override with a macro, e.g. special timeout for sys_arch_mbox_fetch() */
+#ifndef sys_arch_mbox_tryfetch
+/**
+ * @ingroup sys_mbox
+ * Wait for a new message to arrive in the mbox
+ * @param mbox mbox to get a message from
+ * @param msg pointer where the message is stored
+ * @return 0 (milliseconds) if a message has been received
+ * or SYS_MBOX_EMPTY if the mailbox is empty
+ */
+u32_t sys_arch_mbox_tryfetch(sys_mbox_t *mbox, void **msg);
+#endif
+/**
+ * For now, we map straight to sys_arch implementation.
+ */
+#define sys_mbox_tryfetch(mbox, msg) sys_arch_mbox_tryfetch(mbox, msg)
+/**
+ * @ingroup sys_mbox
+ * Delete an mbox
+ * @param mbox mbox to delete
+ */
+void sys_mbox_free(sys_mbox_t *mbox);
+#define sys_mbox_fetch(mbox, msg) sys_arch_mbox_fetch(mbox, msg, 0)
+#ifndef sys_mbox_valid
+/**
+ * @ingroup sys_mbox
+ * Check if an mbox is valid/allocated: return 1 for valid, 0 for invalid
+ */
+int sys_mbox_valid(sys_mbox_t *mbox);
+#endif
+#ifndef sys_mbox_set_invalid
+/**
+ * @ingroup sys_mbox
+ * Set an mbox invalid so that sys_mbox_valid returns 0
+ */
+void sys_mbox_set_invalid(sys_mbox_t *mbox);
+#endif
+#ifndef sys_mbox_valid_val
+/**
+ * Same as sys_mbox_valid() but taking a value, not a pointer
+ */
+#define sys_mbox_valid_val(mbox) sys_mbox_valid(&(mbox))
+#endif
+#ifndef sys_mbox_set_invalid_val
+/**
+ * Same as sys_mbox_set_invalid() but taking a value, not a pointer
+ */
+#define sys_mbox_set_invalid_val(mbox) sys_mbox_set_invalid(&(mbox))
+#endif
+
+
+/**
+ * @ingroup sys_misc
+ * The only thread function:
+ * Creates a new thread
+ * ATTENTION: although this function returns a value, it MUST NOT FAIL (ports have to assert this!)
+ * @param name human-readable name for the thread (used for debugging purposes)
+ * @param thread thread-function
+ * @param arg parameter passed to 'thread'
+ * @param stacksize stack size in bytes for the new thread (may be ignored by ports)
+ * @param prio priority of the new thread (may be ignored by ports) */
+sys_thread_t sys_thread_new(const char *name, lwip_thread_fn thread, void *arg, int stacksize, int prio);
+
+#endif /* NO_SYS */
+
+/* sys_init() must be called before anything else. */
+void sys_init(void);
+
+#ifndef sys_jiffies
+/**
+ * Ticks/jiffies since power up.
+ */
+u32_t sys_jiffies(void);
+#endif
+
+/**
+ * @ingroup sys_time
+ * Returns the current time in milliseconds,
+ * may be the same as sys_jiffies or at least based on it.
+ */
+u32_t sys_now(void);
+
+/* Critical Region Protection */
+/* These functions must be implemented in the sys_arch.c file.
+ In some implementations they can provide a more light-weight protection
+ mechanism than using semaphores. Otherwise semaphores can be used for
+ implementation */
+#ifndef SYS_ARCH_PROTECT
+/** SYS_LIGHTWEIGHT_PROT
+ * define SYS_LIGHTWEIGHT_PROT in lwipopts.h if you want inter-task protection
+ * for certain critical regions during buffer allocation, deallocation and memory
+ * allocation and deallocation.
+ */
+#if SYS_LIGHTWEIGHT_PROT
+
+/**
+ * @ingroup sys_prot
+ * SYS_ARCH_DECL_PROTECT
+ * declare a protection variable. This macro will default to defining a variable of
+ * type sys_prot_t. If a particular port needs a different implementation, then
+ * this macro may be defined in sys_arch.h.
+ */
+#define SYS_ARCH_DECL_PROTECT(lev) sys_prot_t lev
+/**
+ * @ingroup sys_prot
+ * SYS_ARCH_PROTECT
+ * Perform a "fast" protect. This could be implemented by
+ * disabling interrupts for an embedded system or by using a semaphore or
+ * mutex. The implementation should allow calling SYS_ARCH_PROTECT when
+ * already protected. The old protection level is returned in the variable
+ * "lev". This macro will default to calling the sys_arch_protect() function
+ * which should be implemented in sys_arch.c. If a particular port needs a
+ * different implementation, then this macro may be defined in sys_arch.h
+ */
+#define SYS_ARCH_PROTECT(lev) lev = sys_arch_protect()
+/**
+ * @ingroup sys_prot
+ * SYS_ARCH_UNPROTECT
+ * Perform a "fast" set of the protection level to "lev". This could be
+ * implemented by setting the interrupt level to "lev" within the MACRO or by
+ * using a semaphore or mutex. This macro will default to calling the
+ * sys_arch_unprotect() function which should be implemented in
+ * sys_arch.c. If a particular port needs a different implementation, then
+ * this macro may be defined in sys_arch.h
+ */
+#define SYS_ARCH_UNPROTECT(lev) sys_arch_unprotect(lev)
+sys_prot_t sys_arch_protect(void);
+void sys_arch_unprotect(sys_prot_t pval);
+
+#else
+
+#define SYS_ARCH_DECL_PROTECT(lev)
+#define SYS_ARCH_PROTECT(lev)
+#define SYS_ARCH_UNPROTECT(lev)
+
+#endif /* SYS_LIGHTWEIGHT_PROT */
+
+#endif /* SYS_ARCH_PROTECT */
+
+/*
+ * Macros to set/get and increase/decrease variables in a thread-safe way.
+ * Use these for accessing variable that are used from more than one thread.
+ */
+
+#ifndef SYS_ARCH_INC
+#define SYS_ARCH_INC(var, val) do { \
+ SYS_ARCH_DECL_PROTECT(old_level); \
+ SYS_ARCH_PROTECT(old_level); \
+ var += val; \
+ SYS_ARCH_UNPROTECT(old_level); \
+ } while(0)
+#endif /* SYS_ARCH_INC */
+
+#ifndef SYS_ARCH_DEC
+#define SYS_ARCH_DEC(var, val) do { \
+ SYS_ARCH_DECL_PROTECT(old_level); \
+ SYS_ARCH_PROTECT(old_level); \
+ var -= val; \
+ SYS_ARCH_UNPROTECT(old_level); \
+ } while(0)
+#endif /* SYS_ARCH_DEC */
+
+#ifndef SYS_ARCH_GET
+#define SYS_ARCH_GET(var, ret) do { \
+ SYS_ARCH_DECL_PROTECT(old_level); \
+ SYS_ARCH_PROTECT(old_level); \
+ ret = var; \
+ SYS_ARCH_UNPROTECT(old_level); \
+ } while(0)
+#endif /* SYS_ARCH_GET */
+
+#ifndef SYS_ARCH_SET
+#define SYS_ARCH_SET(var, val) do { \
+ SYS_ARCH_DECL_PROTECT(old_level); \
+ SYS_ARCH_PROTECT(old_level); \
+ var = val; \
+ SYS_ARCH_UNPROTECT(old_level); \
+ } while(0)
+#endif /* SYS_ARCH_SET */
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_SYS_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/tcp.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/tcp.h
new file mode 100644
index 0000000..17e74a3
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/tcp.h
@@ -0,0 +1,435 @@
+/**
+ * @file
+ * TCP API (to be used from TCPIP thread)\n
+ * See also @ref tcp_raw
+ */
+
+/*
+ * 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_HDR_TCP_H
+#define LWIP_HDR_TCP_H
+
+#include "lwip/opt.h"
+
+#if LWIP_TCP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/mem.h"
+#include "lwip/pbuf.h"
+#include "lwip/ip.h"
+#include "lwip/icmp.h"
+#include "lwip/err.h"
+#include "lwip/ip6.h"
+#include "lwip/ip6_addr.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct tcp_pcb;
+
+/** Function prototype for tcp accept callback functions. Called when a new
+ * connection can be accepted on a listening pcb.
+ *
+ * @param arg Additional argument to pass to the callback function (@see tcp_arg())
+ * @param newpcb The new connection pcb
+ * @param err An error code if there has been an error accepting.
+ * Only return ERR_ABRT if you have called tcp_abort from within the
+ * callback function!
+ */
+typedef err_t (*tcp_accept_fn)(void *arg, struct tcp_pcb *newpcb, err_t err);
+
+/** Function prototype for tcp receive callback functions. Called when data has
+ * been received.
+ *
+ * @param arg Additional argument to pass to the callback function (@see tcp_arg())
+ * @param tpcb The connection pcb which received data
+ * @param p The received data (or NULL when the connection has been closed!)
+ * @param err An error code if there has been an error receiving
+ * Only return ERR_ABRT if you have called tcp_abort from within the
+ * callback function!
+ */
+typedef err_t (*tcp_recv_fn)(void *arg, struct tcp_pcb *tpcb,
+ struct pbuf *p, err_t err);
+
+/** Function prototype for tcp sent callback functions. Called when sent data has
+ * been acknowledged by the remote side. Use it to free corresponding resources.
+ * This also means that the pcb has now space available to send new data.
+ *
+ * @param arg Additional argument to pass to the callback function (@see tcp_arg())
+ * @param tpcb The connection pcb for which data has been acknowledged
+ * @param len The amount of bytes acknowledged
+ * @return ERR_OK: try to send some data by calling tcp_output
+ * Only return ERR_ABRT if you have called tcp_abort from within the
+ * callback function!
+ */
+typedef err_t (*tcp_sent_fn)(void *arg, struct tcp_pcb *tpcb,
+ u16_t len);
+
+/** Function prototype for tcp poll callback functions. Called periodically as
+ * specified by @see tcp_poll.
+ *
+ * @param arg Additional argument to pass to the callback function (@see tcp_arg())
+ * @param tpcb tcp pcb
+ * @return ERR_OK: try to send some data by calling tcp_output
+ * Only return ERR_ABRT if you have called tcp_abort from within the
+ * callback function!
+ */
+typedef err_t (*tcp_poll_fn)(void *arg, struct tcp_pcb *tpcb);
+
+/** Function prototype for tcp error callback functions. Called when the pcb
+ * receives a RST or is unexpectedly closed for any other reason.
+ *
+ * @note The corresponding pcb is already freed when this callback is called!
+ *
+ * @param arg Additional argument to pass to the callback function (@see tcp_arg())
+ * @param err Error code to indicate why the pcb has been closed
+ * ERR_ABRT: aborted through tcp_abort or by a TCP timer
+ * ERR_RST: the connection was reset by the remote host
+ */
+typedef void (*tcp_err_fn)(void *arg, err_t err);
+
+/** Function prototype for tcp connected callback functions. Called when a pcb
+ * is connected to the remote side after initiating a connection attempt by
+ * calling tcp_connect().
+ *
+ * @param arg Additional argument to pass to the callback function (@see tcp_arg())
+ * @param tpcb The connection pcb which is connected
+ * @param err An unused error code, always ERR_OK currently ;-) @todo!
+ * Only return ERR_ABRT if you have called tcp_abort from within the
+ * callback function!
+ *
+ * @note When a connection attempt fails, the error callback is currently called!
+ */
+typedef err_t (*tcp_connected_fn)(void *arg, struct tcp_pcb *tpcb, err_t err);
+
+#if LWIP_WND_SCALE
+#define RCV_WND_SCALE(pcb, wnd) (((wnd) >> (pcb)->rcv_scale))
+#define SND_WND_SCALE(pcb, wnd) (((wnd) << (pcb)->snd_scale))
+#define TCPWND16(x) ((u16_t)LWIP_MIN((x), 0xFFFF))
+#define TCP_WND_MAX(pcb) ((tcpwnd_size_t)(((pcb)->flags & TF_WND_SCALE) ? TCP_WND : TCPWND16(TCP_WND)))
+typedef u32_t tcpwnd_size_t;
+#else
+#define RCV_WND_SCALE(pcb, wnd) (wnd)
+#define SND_WND_SCALE(pcb, wnd) (wnd)
+#define TCPWND16(x) (x)
+#define TCP_WND_MAX(pcb) TCP_WND
+typedef u16_t tcpwnd_size_t;
+#endif
+
+#if LWIP_WND_SCALE || TCP_LISTEN_BACKLOG || LWIP_TCP_TIMESTAMPS
+typedef u16_t tcpflags_t;
+#else
+typedef u8_t tcpflags_t;
+#endif
+
+enum tcp_state {
+ CLOSED = 0,
+ LISTEN = 1,
+ SYN_SENT = 2,
+ SYN_RCVD = 3,
+ ESTABLISHED = 4,
+ FIN_WAIT_1 = 5,
+ FIN_WAIT_2 = 6,
+ CLOSE_WAIT = 7,
+ CLOSING = 8,
+ LAST_ACK = 9,
+ TIME_WAIT = 10
+};
+
+/**
+ * members common to struct tcp_pcb and struct tcp_listen_pcb
+ */
+#define TCP_PCB_COMMON(type) \
+ type *next; /* for the linked list */ \
+ void *callback_arg; \
+ enum tcp_state state; /* TCP state */ \
+ u8_t prio; \
+ /* ports are in host byte order */ \
+ u16_t local_port
+
+
+/** the TCP protocol control block for listening pcbs */
+struct tcp_pcb_listen {
+/** Common members of all PCB types */
+ IP_PCB;
+/** Protocol specific PCB members */
+ TCP_PCB_COMMON(struct tcp_pcb_listen);
+
+#if LWIP_CALLBACK_API
+ /* Function to call when a listener has been connected. */
+ tcp_accept_fn accept;
+#endif /* LWIP_CALLBACK_API */
+
+#if TCP_LISTEN_BACKLOG
+ u8_t backlog;
+ u8_t accepts_pending;
+#endif /* TCP_LISTEN_BACKLOG */
+};
+
+
+/** the TCP protocol control block */
+struct tcp_pcb {
+/** common PCB members */
+ IP_PCB;
+/** protocol specific PCB members */
+ TCP_PCB_COMMON(struct tcp_pcb);
+
+ /* ports are in host byte order */
+ u16_t remote_port;
+
+ tcpflags_t flags;
+#define TF_ACK_DELAY 0x01U /* Delayed ACK. */
+#define TF_ACK_NOW 0x02U /* Immediate ACK. */
+#define TF_INFR 0x04U /* In fast recovery. */
+#define TF_CLOSEPEND 0x08U /* If this is set, tcp_close failed to enqueue the FIN (retried in tcp_tmr) */
+#define TF_RXCLOSED 0x10U /* rx closed by tcp_shutdown */
+#define TF_FIN 0x20U /* Connection was closed locally (FIN segment enqueued). */
+#define TF_NODELAY 0x40U /* Disable Nagle algorithm */
+#define TF_NAGLEMEMERR 0x80U /* nagle enabled, memerr, try to output to prevent delayed ACK to happen */
+#if LWIP_WND_SCALE
+#define TF_WND_SCALE 0x0100U /* Window Scale option enabled */
+#endif
+#if TCP_LISTEN_BACKLOG
+#define TF_BACKLOGPEND 0x0200U /* If this is set, a connection pcb has increased the backlog on its listener */
+#endif
+#if LWIP_TCP_TIMESTAMPS
+#define TF_TIMESTAMP 0x0400U /* Timestamp option enabled */
+#endif
+
+ /* 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 */
+ tcpwnd_size_t rcv_wnd; /* receiver window available */
+ tcpwnd_size_t rcv_ann_wnd; /* receiver window to announce */
+ u32_t rcv_ann_right_edge; /* announced right edge of window */
+
+ /* Retransmission timer. */
+ s16_t rtime;
+
+ u16_t mss; /* maximum segment size */
+
+ /* RTT (round trip time) estimation variables */
+ u32_t rttest; /* RTT estimate in 500ms ticks */
+ u32_t rtseq; /* sequence number being timed */
+ s16_t sa, sv; /* @todo document this */
+
+ s16_t rto; /* retransmission time-out */
+ u8_t nrtx; /* number of retransmissions */
+
+ /* fast retransmit/recovery */
+ u8_t dupacks;
+ u32_t lastack; /* Highest acknowledged seqno. */
+
+ /* congestion avoidance/control variables */
+ tcpwnd_size_t cwnd;
+ tcpwnd_size_t ssthresh;
+
+ /* sender variables */
+ u32_t snd_nxt; /* next new seqno to be sent */
+ 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. */
+ tcpwnd_size_t snd_wnd; /* sender window */
+ tcpwnd_size_t snd_wnd_max; /* the maximum sender window announced by the remote host */
+
+ tcpwnd_size_t snd_buf; /* Available buffer space for sending (in bytes). */
+#define TCP_SNDQUEUELEN_OVERFLOW (0xffffU-3)
+ u16_t snd_queuelen; /* Number of pbufs currently in the send buffer. */
+
+#if TCP_OVERSIZE
+ /* Extra bytes available at the end of the last pbuf in unsent. */
+ u16_t unsent_oversize;
+#endif /* TCP_OVERSIZE */
+
+ /* These are ordered by sequence number: */
+ struct tcp_seg *unsent; /* Unsent (queued) segments. */
+ struct tcp_seg *unacked; /* Sent but unacknowledged segments. */
+#if TCP_QUEUE_OOSEQ
+ struct tcp_seg *ooseq; /* Received out of sequence segments. */
+#endif /* TCP_QUEUE_OOSEQ */
+
+ struct pbuf *refused_data; /* Data previously received but not yet taken by upper layer */
+
+#if LWIP_CALLBACK_API || TCP_LISTEN_BACKLOG
+ struct tcp_pcb_listen* listener;
+#endif /* LWIP_CALLBACK_API || TCP_LISTEN_BACKLOG */
+
+#if LWIP_CALLBACK_API
+ /* Function to be called when more send buffer space is available. */
+ tcp_sent_fn sent;
+ /* Function to be called when (in-sequence) data has arrived. */
+ tcp_recv_fn recv;
+ /* Function to be called when a connection has been set up. */
+ tcp_connected_fn connected;
+ /* Function which is called periodically. */
+ tcp_poll_fn poll;
+ /* Function to be called whenever a fatal error occurs. */
+ tcp_err_fn errf;
+#endif /* LWIP_CALLBACK_API */
+
+#if LWIP_TCP_TIMESTAMPS
+ u32_t ts_lastacksent;
+ u32_t ts_recent;
+#endif /* LWIP_TCP_TIMESTAMPS */
+
+ /* idle time before KEEPALIVE is sent */
+ u32_t keep_idle;
+#if LWIP_TCP_KEEPALIVE
+ u32_t keep_intvl;
+ u32_t keep_cnt;
+#endif /* LWIP_TCP_KEEPALIVE */
+
+ /* Persist timer counter */
+ u8_t persist_cnt;
+ /* Persist timer back-off */
+ u8_t persist_backoff;
+
+ /* KEEPALIVE counter */
+ u8_t keep_cnt_sent;
+
+#if LWIP_WND_SCALE
+ u8_t snd_scale;
+ u8_t rcv_scale;
+#endif
+};
+
+#if LWIP_EVENT_API
+
+enum lwip_event {
+ LWIP_EVENT_ACCEPT,
+ LWIP_EVENT_SENT,
+ LWIP_EVENT_RECV,
+ LWIP_EVENT_CONNECTED,
+ LWIP_EVENT_POLL,
+ LWIP_EVENT_ERR
+};
+
+err_t lwip_tcp_event(void *arg, struct tcp_pcb *pcb,
+ enum lwip_event,
+ struct pbuf *p,
+ u16_t size,
+ err_t err);
+
+#endif /* LWIP_EVENT_API */
+
+/* Application program's interface: */
+struct tcp_pcb * tcp_new (void);
+struct tcp_pcb * tcp_new_ip_type (u8_t type);
+
+void tcp_arg (struct tcp_pcb *pcb, void *arg);
+#if LWIP_CALLBACK_API
+void tcp_recv (struct tcp_pcb *pcb, tcp_recv_fn recv);
+void tcp_sent (struct tcp_pcb *pcb, tcp_sent_fn sent);
+void tcp_err (struct tcp_pcb *pcb, tcp_err_fn err);
+void tcp_accept (struct tcp_pcb *pcb, tcp_accept_fn accept);
+#endif /* LWIP_CALLBACK_API */
+void tcp_poll (struct tcp_pcb *pcb, tcp_poll_fn poll, u8_t interval);
+
+#if LWIP_TCP_TIMESTAMPS
+#define tcp_mss(pcb) (((pcb)->flags & TF_TIMESTAMP) ? ((pcb)->mss - 12) : (pcb)->mss)
+#else /* LWIP_TCP_TIMESTAMPS */
+#define tcp_mss(pcb) ((pcb)->mss)
+#endif /* LWIP_TCP_TIMESTAMPS */
+#define tcp_sndbuf(pcb) (TCPWND16((pcb)->snd_buf))
+#define tcp_sndqueuelen(pcb) ((pcb)->snd_queuelen)
+/** @ingroup tcp_raw */
+#define tcp_nagle_disable(pcb) ((pcb)->flags |= TF_NODELAY)
+/** @ingroup tcp_raw */
+#define tcp_nagle_enable(pcb) ((pcb)->flags = (tcpflags_t)((pcb)->flags & ~TF_NODELAY))
+/** @ingroup tcp_raw */
+#define tcp_nagle_disabled(pcb) (((pcb)->flags & TF_NODELAY) != 0)
+
+#if TCP_LISTEN_BACKLOG
+/*lint -save -e607 */
+#define tcp_backlog_set(pcb, new_backlog) do { \
+ LWIP_ASSERT("pcb->state == LISTEN (called for wrong pcb?)", (pcb)->state == LISTEN); \
+ ((struct tcp_pcb_listen *)(pcb))->backlog = ((new_backlog) ? (new_backlog) : 1); } while(0)
+/*lint -restore */
+void tcp_backlog_delayed(struct tcp_pcb* pcb);
+void tcp_backlog_accepted(struct tcp_pcb* pcb);
+#else /* TCP_LISTEN_BACKLOG */
+#define tcp_backlog_set(pcb, new_backlog)
+#define tcp_backlog_delayed(pcb)
+#define tcp_backlog_accepted(pcb)
+#endif /* TCP_LISTEN_BACKLOG */
+#define tcp_accepted(pcb) /* compatibility define, not needed any more */
+
+void tcp_recved (struct tcp_pcb *pcb, u16_t len);
+err_t tcp_bind (struct tcp_pcb *pcb, const ip_addr_t *ipaddr,
+ u16_t port);
+err_t tcp_connect (struct tcp_pcb *pcb, const ip_addr_t *ipaddr,
+ u16_t port, tcp_connected_fn connected);
+
+struct tcp_pcb * tcp_listen_with_backlog_and_err(struct tcp_pcb *pcb, u8_t backlog, err_t *err);
+struct tcp_pcb * tcp_listen_with_backlog(struct tcp_pcb *pcb, u8_t backlog);
+/** @ingroup tcp_raw */
+#define tcp_listen(pcb) tcp_listen_with_backlog(pcb, TCP_DEFAULT_LISTEN_BACKLOG)
+
+void tcp_abort (struct tcp_pcb *pcb);
+err_t tcp_close (struct tcp_pcb *pcb);
+err_t tcp_shutdown(struct tcp_pcb *pcb, int shut_rx, int shut_tx);
+
+/* Flags for "apiflags" parameter in tcp_write */
+#define TCP_WRITE_FLAG_COPY 0x01
+#define TCP_WRITE_FLAG_MORE 0x02
+
+err_t tcp_write (struct tcp_pcb *pcb, const void *dataptr, u16_t len,
+ u8_t apiflags);
+
+void tcp_setprio (struct tcp_pcb *pcb, u8_t prio);
+
+#define TCP_PRIO_MIN 1
+#define TCP_PRIO_NORMAL 64
+#define TCP_PRIO_MAX 127
+
+err_t tcp_output (struct tcp_pcb *pcb);
+
+
+const char* tcp_debug_state_str(enum tcp_state s);
+
+/* for compatibility with older implementation */
+#define tcp_new_ip6() tcp_new_ip_type(IPADDR_TYPE_V6)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_TCP */
+
+#endif /* LWIP_HDR_TCP_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/tcpip.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/tcpip.h
new file mode 100644
index 0000000..f2f6b46
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/tcpip.h
@@ -0,0 +1,106 @@
+/**
+ * @file
+ * Functions to sync with TCPIP thread
+ */
+
+/*
+ * 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_HDR_TCPIP_H
+#define LWIP_HDR_TCPIP_H
+
+#include "lwip/opt.h"
+
+#if !NO_SYS /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/err.h"
+#include "lwip/timeouts.h"
+#include "lwip/netif.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if LWIP_TCPIP_CORE_LOCKING
+/** The global semaphore to lock the stack. */
+extern sys_mutex_t lock_tcpip_core;
+/** Lock lwIP core mutex (needs @ref LWIP_TCPIP_CORE_LOCKING 1) */
+#define LOCK_TCPIP_CORE() sys_mutex_lock(&lock_tcpip_core)
+/** Unlock lwIP core mutex (needs @ref LWIP_TCPIP_CORE_LOCKING 1) */
+#define UNLOCK_TCPIP_CORE() sys_mutex_unlock(&lock_tcpip_core)
+#else /* LWIP_TCPIP_CORE_LOCKING */
+#define LOCK_TCPIP_CORE()
+#define UNLOCK_TCPIP_CORE()
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+
+struct pbuf;
+struct netif;
+
+/** Function prototype for the init_done function passed to tcpip_init */
+typedef void (*tcpip_init_done_fn)(void *arg);
+/** Function prototype for functions passed to tcpip_callback() */
+typedef void (*tcpip_callback_fn)(void *ctx);
+
+/* Forward declarations */
+struct tcpip_callback_msg;
+
+void tcpip_init(tcpip_init_done_fn tcpip_init_done, void *arg);
+
+err_t tcpip_inpkt(struct pbuf *p, struct netif *inp, netif_input_fn input_fn);
+err_t tcpip_input(struct pbuf *p, struct netif *inp);
+
+err_t tcpip_callback_with_block(tcpip_callback_fn function, void *ctx, u8_t block);
+/**
+ * @ingroup lwip_os
+ * @see tcpip_callback_with_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);
+
+#if LWIP_TCPIP_TIMEOUT && LWIP_TIMERS
+err_t tcpip_timeout(u32_t msecs, sys_timeout_handler h, void *arg);
+err_t tcpip_untimeout(sys_timeout_handler h, void *arg);
+#endif /* LWIP_TCPIP_TIMEOUT && LWIP_TIMERS */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !NO_SYS */
+
+#endif /* LWIP_HDR_TCPIP_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/timeouts.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/timeouts.h
new file mode 100644
index 0000000..c9b93aa
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/timeouts.h
@@ -0,0 +1,121 @@
+/**
+ * @file
+ * Timer implementations
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ * Simon Goldschmidt
+ *
+ */
+#ifndef LWIP_HDR_TIMEOUTS_H
+#define LWIP_HDR_TIMEOUTS_H
+
+#include "lwip/opt.h"
+#include "lwip/err.h"
+#if !NO_SYS
+#include "lwip/sys.h"
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef LWIP_DEBUG_TIMERNAMES
+#ifdef LWIP_DEBUG
+#define LWIP_DEBUG_TIMERNAMES SYS_DEBUG
+#else /* LWIP_DEBUG */
+#define LWIP_DEBUG_TIMERNAMES 0
+#endif /* LWIP_DEBUG*/
+#endif
+
+/** Function prototype for a stack-internal timer function that has to be
+ * called at a defined interval */
+typedef void (* lwip_cyclic_timer_handler)(void);
+
+/** This struct contains information about a stack-internal timer function
+ that has to be called at a defined interval */
+struct lwip_cyclic_timer {
+ u32_t interval_ms;
+ lwip_cyclic_timer_handler handler;
+#if LWIP_DEBUG_TIMERNAMES
+ const char* handler_name;
+#endif /* LWIP_DEBUG_TIMERNAMES */
+};
+
+/** This array contains all stack-internal cyclic timers. To get the number of
+ * timers, use LWIP_ARRAYSIZE() */
+extern const struct lwip_cyclic_timer lwip_cyclic_timers[];
+
+#if LWIP_TIMERS
+
+/** Function prototype for a timeout callback function. Register such a function
+ * using sys_timeout().
+ *
+ * @param arg Additional argument to pass to the function - set up by sys_timeout()
+ */
+typedef void (* sys_timeout_handler)(void *arg);
+
+struct sys_timeo {
+ struct sys_timeo *next;
+ u32_t time;
+ sys_timeout_handler h;
+ void *arg;
+#if LWIP_DEBUG_TIMERNAMES
+ const char* handler_name;
+#endif /* LWIP_DEBUG_TIMERNAMES */
+};
+
+void sys_timeouts_init(void);
+
+#if LWIP_DEBUG_TIMERNAMES
+void sys_timeout_debug(u32_t msecs, sys_timeout_handler handler, void *arg, const char* handler_name);
+#define sys_timeout(msecs, handler, arg) sys_timeout_debug(msecs, handler, arg, #handler)
+#else /* LWIP_DEBUG_TIMERNAMES */
+void sys_timeout(u32_t msecs, sys_timeout_handler handler, void *arg);
+#endif /* LWIP_DEBUG_TIMERNAMES */
+
+void sys_untimeout(sys_timeout_handler handler, void *arg);
+void sys_restart_timeouts(void);
+#if NO_SYS
+void sys_check_timeouts(void);
+u32_t sys_timeouts_sleeptime(void);
+#else /* NO_SYS */
+void sys_timeouts_mbox_fetch(sys_mbox_t *mbox, void **msg);
+#endif /* NO_SYS */
+
+
+#endif /* LWIP_TIMERS */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_TIMEOUTS_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/timers.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/timers.h
new file mode 100644
index 0000000..629c1e7
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/timers.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ * Simon Goldschmidt
+ *
+ */
+#ifndef LWIP_HDR_TIMERS_H
+#define LWIP_HDR_TIMERS_H
+
+#include "lwip/opt.h"
+
+/* Timers are not supported when NO_SYS==1 and NO_SYS_NO_TIMERS==1 */
+#define LWIP_TIMERS (!NO_SYS || (NO_SYS && !NO_SYS_NO_TIMERS))
+
+#if LWIP_TIMERS
+
+#include "lwip/err.h"
+#if !NO_SYS
+#include "lwip/sys.h"
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef LWIP_DEBUG_TIMERNAMES
+#ifdef LWIP_DEBUG
+#define LWIP_DEBUG_TIMERNAMES SYS_DEBUG
+#else /* LWIP_DEBUG */
+#define LWIP_DEBUG_TIMERNAMES 0
+#endif /* LWIP_DEBUG*/
+#endif
+
+/** Function prototype for a timeout callback function. Register such a function
+ * using sys_timeout().
+ *
+ * @param arg Additional argument to pass to the function - set up by sys_timeout()
+ */
+typedef void (* sys_timeout_handler)(void *arg);
+
+struct sys_timeo {
+ struct sys_timeo *next;
+ u32_t time;
+ sys_timeout_handler h;
+ void *arg;
+#if LWIP_DEBUG_TIMERNAMES
+ const char* handler_name;
+#endif /* LWIP_DEBUG_TIMERNAMES */
+};
+
+void sys_timeouts_init(void);
+
+#if LWIP_DEBUG_TIMERNAMES
+void sys_timeout_debug(u32_t msecs, sys_timeout_handler handler, void *arg, const char* handler_name);
+#define sys_timeout(msecs, handler, arg) sys_timeout_debug(msecs, handler, arg, #handler)
+#else /* LWIP_DEBUG_TIMERNAMES */
+void sys_timeout(u32_t msecs, sys_timeout_handler handler, void *arg);
+#endif /* LWIP_DEBUG_TIMERNAMES */
+
+void sys_untimeout(sys_timeout_handler handler, void *arg);
+#if NO_SYS
+void sys_check_timeouts(void);
+void sys_restart_timeouts(void);
+u32_t sys_timeouts_sleeptime(void);
+#else /* NO_SYS */
+void sys_timeouts_mbox_fetch(sys_mbox_t *mbox, void **msg);
+#endif /* NO_SYS */
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_TIMERS */
+#endif /* LWIP_HDR_TIMERS_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/udp.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/udp.h
new file mode 100644
index 0000000..b929907
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/lwip/udp.h
@@ -0,0 +1,182 @@
+/**
+ * @file
+ * UDP API (to be used from TCPIP thread)\n
+ * See also @ref udp_raw
+ */
+
+/*
+ * 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_HDR_UDP_H
+#define LWIP_HDR_UDP_H
+
+#include "lwip/opt.h"
+
+#if LWIP_UDP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/pbuf.h"
+#include "lwip/netif.h"
+#include "lwip/ip_addr.h"
+#include "lwip/ip.h"
+#include "lwip/ip6_addr.h"
+#include "lwip/prot/udp.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define UDP_FLAGS_NOCHKSUM 0x01U
+#define UDP_FLAGS_UDPLITE 0x02U
+#define UDP_FLAGS_CONNECTED 0x04U
+#define UDP_FLAGS_MULTICAST_LOOP 0x08U
+
+struct udp_pcb;
+
+/** Function prototype for udp pcb receive callback functions
+ * addr and port are in same byte order as in the pcb
+ * The callback is responsible for freeing the pbuf
+ * if it's not used any more.
+ *
+ * ATTENTION: Be aware that 'addr' might point into the pbuf 'p' so freeing this pbuf
+ * can make 'addr' invalid, too.
+ *
+ * @param arg user supplied argument (udp_pcb.recv_arg)
+ * @param pcb the udp_pcb which received data
+ * @param p the packet buffer that was received
+ * @param addr the remote IP address from which the packet was received
+ * @param port the remote port from which the packet was received
+ */
+typedef void (*udp_recv_fn)(void *arg, struct udp_pcb *pcb, struct pbuf *p,
+ const ip_addr_t *addr, u16_t port);
+
+/** the UDP protocol control block */
+struct udp_pcb {
+/** Common members of all PCB types */
+ IP_PCB;
+
+/* Protocol specific PCB members */
+
+ struct udp_pcb *next;
+
+ u8_t flags;
+ /** ports are in host byte order */
+ u16_t local_port, remote_port;
+
+#if LWIP_MULTICAST_TX_OPTIONS
+ /** outgoing network interface for multicast packets */
+ ip_addr_t multicast_ip;
+ /** TTL for outgoing multicast packets */
+ u8_t mcast_ttl;
+#endif /* LWIP_MULTICAST_TX_OPTIONS */
+
+#if LWIP_UDPLITE
+ /** used for UDP_LITE only */
+ u16_t chksum_len_rx, chksum_len_tx;
+#endif /* LWIP_UDPLITE */
+
+ /** receive callback function */
+ udp_recv_fn recv;
+ /** user-supplied argument for the recv callback */
+ void *recv_arg;
+};
+/* udp_pcbs export for external reference (e.g. SNMP agent) */
+extern struct udp_pcb *udp_pcbs;
+
+/* The following functions is the application layer interface to the
+ UDP code. */
+struct udp_pcb * udp_new (void);
+struct udp_pcb * udp_new_ip_type(u8_t type);
+void udp_remove (struct udp_pcb *pcb);
+err_t udp_bind (struct udp_pcb *pcb, const ip_addr_t *ipaddr,
+ u16_t port);
+err_t udp_connect (struct udp_pcb *pcb, const ip_addr_t *ipaddr,
+ u16_t port);
+void udp_disconnect (struct udp_pcb *pcb);
+void udp_recv (struct udp_pcb *pcb, udp_recv_fn recv,
+ void *recv_arg);
+err_t udp_sendto_if (struct udp_pcb *pcb, struct pbuf *p,
+ const ip_addr_t *dst_ip, u16_t dst_port,
+ struct netif *netif);
+err_t udp_sendto_if_src(struct udp_pcb *pcb, struct pbuf *p,
+ const ip_addr_t *dst_ip, u16_t dst_port,
+ struct netif *netif, const ip_addr_t *src_ip);
+err_t udp_sendto (struct udp_pcb *pcb, struct pbuf *p,
+ const ip_addr_t *dst_ip, u16_t dst_port);
+err_t udp_send (struct udp_pcb *pcb, struct pbuf *p);
+
+#if LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP
+err_t udp_sendto_if_chksum(struct udp_pcb *pcb, struct pbuf *p,
+ const ip_addr_t *dst_ip, u16_t dst_port,
+ struct netif *netif, u8_t have_chksum,
+ u16_t chksum);
+err_t udp_sendto_chksum(struct udp_pcb *pcb, struct pbuf *p,
+ const ip_addr_t *dst_ip, u16_t dst_port,
+ u8_t have_chksum, u16_t chksum);
+err_t udp_send_chksum(struct udp_pcb *pcb, struct pbuf *p,
+ u8_t have_chksum, u16_t chksum);
+err_t udp_sendto_if_src_chksum(struct udp_pcb *pcb, struct pbuf *p,
+ const ip_addr_t *dst_ip, u16_t dst_port, struct netif *netif,
+ u8_t have_chksum, u16_t chksum, const ip_addr_t *src_ip);
+#endif /* LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP */
+
+#define udp_flags(pcb) ((pcb)->flags)
+#define udp_setflags(pcb, f) ((pcb)->flags = (f))
+
+/* The following functions are the lower layer interface to UDP. */
+void udp_input (struct pbuf *p, struct netif *inp);
+
+void udp_init (void);
+
+/* for compatibility with older implementation */
+#define udp_new_ip6() udp_new_ip_type(IPADDR_TYPE_V6)
+
+#if LWIP_MULTICAST_TX_OPTIONS
+#define udp_set_multicast_netif_addr(pcb, ip4addr) ip_addr_copy_from_ip4((pcb)->multicast_ip, *(ip4addr))
+#define udp_get_multicast_netif_addr(pcb) ip_2_ip4(&(pcb)->multicast_ip)
+#define udp_set_multicast_ttl(pcb, value) do { (pcb)->mcast_ttl = value; } while(0)
+#define udp_get_multicast_ttl(pcb) ((pcb)->mcast_ttl)
+#endif /* LWIP_MULTICAST_TX_OPTIONS */
+
+#if UDP_DEBUG
+void udp_debug_print(struct udp_hdr *udphdr);
+#else
+#define udp_debug_print(udphdr)
+#endif
+
+void udp_netif_ip_addr_changed(const ip_addr_t* old_addr, const ip_addr_t* new_addr);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_UDP */
+
+#endif /* LWIP_HDR_UDP_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/etharp.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/etharp.h
new file mode 100644
index 0000000..b536fd2
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/etharp.h
@@ -0,0 +1,3 @@
+/* ARP has been moved to core/ipv4, provide this #include for compatibility only */
+#include "lwip/etharp.h"
+#include "netif/ethernet.h"
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ethernet.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ethernet.h
new file mode 100644
index 0000000..49649cb
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ethernet.h
@@ -0,0 +1,77 @@
+/**
+ * @file
+ * Ethernet input function - handles INCOMING ethernet level traffic
+ * To be used in most low-level netif implementations
+ */
+
+/*
+ * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
+ * Copyright (c) 2003-2004 Leon Woestenberg <leon.woestenberg@axon.tv>
+ * Copyright (c) 2003-2004 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#ifndef LWIP_HDR_NETIF_ETHERNET_H
+#define LWIP_HDR_NETIF_ETHERNET_H
+
+#include "lwip/opt.h"
+
+#include "lwip/pbuf.h"
+#include "lwip/netif.h"
+#include "lwip/prot/ethernet.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if LWIP_ARP || LWIP_ETHERNET
+
+/** Define this to 1 and define LWIP_ARP_FILTER_NETIF_FN(pbuf, netif, type)
+ * to a filter function that returns the correct netif when using multiple
+ * netifs on one hardware interface where the netif's low-level receive
+ * routine cannot decide for the correct netif (e.g. when mapping multiple
+ * IP addresses to one hardware interface).
+ */
+#ifndef LWIP_ARP_FILTER_NETIF
+#define LWIP_ARP_FILTER_NETIF 0
+#endif
+
+err_t ethernet_input(struct pbuf *p, struct netif *netif);
+err_t ethernet_output(struct netif* netif, struct pbuf* p, const struct eth_addr* src, const struct eth_addr* dst, u16_t eth_type);
+
+extern const struct eth_addr ethbroadcast, ethzero;
+
+#endif /* LWIP_ARP || LWIP_ETHERNET */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_NETIF_ETHERNET_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/lowpan6.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/lowpan6.h
new file mode 100644
index 0000000..4174644
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/lowpan6.h
@@ -0,0 +1,86 @@
+/**
+ * @file
+ *
+ * 6LowPAN output for IPv6. Uses ND tables for link-layer addressing. Fragments packets to 6LowPAN units.
+ */
+
+/*
+ * Copyright (c) 2015 Inico Technologies Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Ivan Delamer <delamer@inicotech.com>
+ *
+ *
+ * Please coordinate changes and requests with Ivan Delamer
+ * <delamer@inicotech.com>
+ */
+
+#ifndef LWIP_HDR_LOWPAN6_H
+#define LWIP_HDR_LOWPAN6_H
+
+#include "netif/lowpan6_opts.h"
+
+#if LWIP_IPV6 && LWIP_6LOWPAN /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/pbuf.h"
+#include "lwip/ip.h"
+#include "lwip/ip_addr.h"
+#include "lwip/netif.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** 1 second period */
+#define LOWPAN6_TMR_INTERVAL 1000
+
+void lowpan6_tmr(void);
+
+err_t lowpan6_set_context(u8_t index, const ip6_addr_t * context);
+err_t lowpan6_set_short_addr(u8_t addr_high, u8_t addr_low);
+
+#if LWIP_IPV4
+err_t lowpan4_output(struct netif *netif, struct pbuf *q, const ip4_addr_t *ipaddr);
+#endif /* LWIP_IPV4 */
+err_t lowpan6_output(struct netif *netif, struct pbuf *q, const ip6_addr_t *ip6addr);
+err_t lowpan6_input(struct pbuf * p, struct netif *netif);
+err_t lowpan6_if_init(struct netif *netif);
+
+/* pan_id in network byte order. */
+err_t lowpan6_set_pan_id(u16_t pan_id);
+
+#if !NO_SYS
+err_t tcpip_6lowpan_input(struct pbuf *p, struct netif *inp);
+#endif /* !NO_SYS */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_IPV6 && LWIP_6LOWPAN */
+
+#endif /* LWIP_HDR_LOWPAN6_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/lowpan6_opts.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/lowpan6_opts.h
new file mode 100644
index 0000000..fb93ea0
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/lowpan6_opts.h
@@ -0,0 +1,70 @@
+/**
+ * @file
+ * 6LowPAN options list
+ */
+
+/*
+ * Copyright (c) 2015 Inico Technologies Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Ivan Delamer <delamer@inicotech.com>
+ *
+ *
+ * Please coordinate changes and requests with Ivan Delamer
+ * <delamer@inicotech.com>
+ */
+
+#ifndef LWIP_HDR_LOWPAN6_OPTS_H
+#define LWIP_HDR_LOWPAN6_OPTS_H
+
+#include "lwip/opt.h"
+
+#ifndef LWIP_6LOWPAN
+#define LWIP_6LOWPAN 0
+#endif
+
+#ifndef LWIP_6LOWPAN_NUM_CONTEXTS
+#define LWIP_6LOWPAN_NUM_CONTEXTS 10
+#endif
+
+#ifndef LWIP_6LOWPAN_INFER_SHORT_ADDRESS
+#define LWIP_6LOWPAN_INFER_SHORT_ADDRESS 1
+#endif
+
+#ifndef LWIP_6LOWPAN_IPHC
+#define LWIP_6LOWPAN_IPHC 1
+#endif
+
+#ifndef LWIP_6LOWPAN_HW_CRC
+#define LWIP_6LOWPAN_HW_CRC 1
+#endif
+
+#ifndef LOWPAN6_DEBUG
+#define LOWPAN6_DEBUG LWIP_DBG_OFF
+#endif
+
+#endif /* LWIP_HDR_LOWPAN6_OPTS_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/ccp.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/ccp.h
new file mode 100644
index 0000000..14dd659
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/ccp.h
@@ -0,0 +1,156 @@
+/*
+ * ccp.h - Definitions for PPP Compression Control Protocol.
+ *
+ * Copyright (c) 1994-2002 Paul Mackerras. 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. The name(s) of the authors of this software must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission.
+ *
+ * 3. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by Paul Mackerras
+ * <paulus@samba.org>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $Id: ccp.h,v 1.12 2004/11/04 10:02:26 paulus Exp $
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT && CCP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#ifndef CCP_H
+#define CCP_H
+
+/*
+ * CCP codes.
+ */
+
+#define CCP_CONFREQ 1
+#define CCP_CONFACK 2
+#define CCP_TERMREQ 5
+#define CCP_TERMACK 6
+#define CCP_RESETREQ 14
+#define CCP_RESETACK 15
+
+/*
+ * Max # bytes for a CCP option
+ */
+
+#define CCP_MAX_OPTION_LENGTH 32
+
+/*
+ * Parts of a CCP packet.
+ */
+
+#define CCP_CODE(dp) ((dp)[0])
+#define CCP_ID(dp) ((dp)[1])
+#define CCP_LENGTH(dp) (((dp)[2] << 8) + (dp)[3])
+#define CCP_HDRLEN 4
+
+#define CCP_OPT_CODE(dp) ((dp)[0])
+#define CCP_OPT_LENGTH(dp) ((dp)[1])
+#define CCP_OPT_MINLEN 2
+
+#if BSDCOMPRESS_SUPPORT
+/*
+ * Definitions for BSD-Compress.
+ */
+
+#define CI_BSD_COMPRESS 21 /* config. option for BSD-Compress */
+#define CILEN_BSD_COMPRESS 3 /* length of config. option */
+
+/* Macros for handling the 3rd byte of the BSD-Compress config option. */
+#define BSD_NBITS(x) ((x) & 0x1F) /* number of bits requested */
+#define BSD_VERSION(x) ((x) >> 5) /* version of option format */
+#define BSD_CURRENT_VERSION 1 /* current version number */
+#define BSD_MAKE_OPT(v, n) (((v) << 5) | (n))
+
+#define BSD_MIN_BITS 9 /* smallest code size supported */
+#define BSD_MAX_BITS 15 /* largest code size supported */
+#endif /* BSDCOMPRESS_SUPPORT */
+
+#if DEFLATE_SUPPORT
+/*
+ * Definitions for Deflate.
+ */
+
+#define CI_DEFLATE 26 /* config option for Deflate */
+#define CI_DEFLATE_DRAFT 24 /* value used in original draft RFC */
+#define CILEN_DEFLATE 4 /* length of its config option */
+
+#define DEFLATE_MIN_SIZE 9
+#define DEFLATE_MAX_SIZE 15
+#define DEFLATE_METHOD_VAL 8
+#define DEFLATE_SIZE(x) (((x) >> 4) + 8)
+#define DEFLATE_METHOD(x) ((x) & 0x0F)
+#define DEFLATE_MAKE_OPT(w) ((((w) - 8) << 4) + DEFLATE_METHOD_VAL)
+#define DEFLATE_CHK_SEQUENCE 0
+#endif /* DEFLATE_SUPPORT */
+
+#if MPPE_SUPPORT
+/*
+ * Definitions for MPPE.
+ */
+
+#define CI_MPPE 18 /* config option for MPPE */
+#define CILEN_MPPE 6 /* length of config option */
+#endif /* MPPE_SUPPORT */
+
+#if PREDICTOR_SUPPORT
+/*
+ * Definitions for other, as yet unsupported, compression methods.
+ */
+
+#define CI_PREDICTOR_1 1 /* config option for Predictor-1 */
+#define CILEN_PREDICTOR_1 2 /* length of its config option */
+#define CI_PREDICTOR_2 2 /* config option for Predictor-2 */
+#define CILEN_PREDICTOR_2 2 /* length of its config option */
+#endif /* PREDICTOR_SUPPORT */
+
+typedef struct ccp_options {
+#if DEFLATE_SUPPORT
+ unsigned int deflate :1; /* do Deflate? */
+ unsigned int deflate_correct :1; /* use correct code for deflate? */
+ unsigned int deflate_draft :1; /* use draft RFC code for deflate? */
+#endif /* DEFLATE_SUPPORT */
+#if BSDCOMPRESS_SUPPORT
+ unsigned int bsd_compress :1; /* do BSD Compress? */
+#endif /* BSDCOMPRESS_SUPPORT */
+#if PREDICTOR_SUPPORT
+ unsigned int predictor_1 :1; /* do Predictor-1? */
+ unsigned int predictor_2 :1; /* do Predictor-2? */
+#endif /* PREDICTOR_SUPPORT */
+
+#if MPPE_SUPPORT
+ u8_t mppe; /* MPPE bitfield */
+#endif /* MPPE_SUPPORT */
+#if BSDCOMPRESS_SUPPORT
+ u_short bsd_bits; /* # bits/code for BSD Compress */
+#endif /* BSDCOMPRESS_SUPPORT */
+#if DEFLATE_SUPPORT
+ u_short deflate_size; /* lg(window size) for Deflate */
+#endif /* DEFLATE_SUPPORT */
+ u8_t method; /* code for chosen compression method */
+} ccp_options;
+
+extern const struct protent ccp_protent;
+
+void ccp_resetrequest(ppp_pcb *pcb); /* Issue a reset-request. */
+
+#endif /* CCP_H */
+#endif /* PPP_SUPPORT && CCP_SUPPORT */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/chap-md5.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/chap-md5.h
new file mode 100644
index 0000000..eb0269f
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/chap-md5.h
@@ -0,0 +1,36 @@
+/*
+ * chap-md5.h - New CHAP/MD5 implementation.
+ *
+ * Copyright (c) 2003 Paul Mackerras. 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. The name(s) of the authors of this software must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission.
+ *
+ * 3. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by Paul Mackerras
+ * <paulus@samba.org>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT && CHAP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+extern const struct chap_digest_type md5_digest;
+
+#endif /* PPP_SUPPORT && CHAP_SUPPORT */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/chap-new.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/chap-new.h
new file mode 100644
index 0000000..64eae32
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/chap-new.h
@@ -0,0 +1,192 @@
+/*
+ * chap-new.c - New CHAP implementation.
+ *
+ * Copyright (c) 2003 Paul Mackerras. 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. The name(s) of the authors of this software must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission.
+ *
+ * 3. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by Paul Mackerras
+ * <paulus@samba.org>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT && CHAP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#ifndef CHAP_H
+#define CHAP_H
+
+#include "ppp.h"
+
+/*
+ * CHAP packets begin with a standard header with code, id, len (2 bytes).
+ */
+#define CHAP_HDRLEN 4
+
+/*
+ * Values for the code field.
+ */
+#define CHAP_CHALLENGE 1
+#define CHAP_RESPONSE 2
+#define CHAP_SUCCESS 3
+#define CHAP_FAILURE 4
+
+/*
+ * CHAP digest codes.
+ */
+#define CHAP_MD5 5
+#if MSCHAP_SUPPORT
+#define CHAP_MICROSOFT 0x80
+#define CHAP_MICROSOFT_V2 0x81
+#endif /* MSCHAP_SUPPORT */
+
+/*
+ * Semi-arbitrary limits on challenge and response fields.
+ */
+#define MAX_CHALLENGE_LEN 64
+#define MAX_RESPONSE_LEN 64
+
+/*
+ * These limits apply to challenge and response packets we send.
+ * The +4 is the +1 that we actually need rounded up.
+ */
+#define CHAL_MAX_PKTLEN (PPP_HDRLEN + CHAP_HDRLEN + 4 + MAX_CHALLENGE_LEN + MAXNAMELEN)
+#define RESP_MAX_PKTLEN (PPP_HDRLEN + CHAP_HDRLEN + 4 + MAX_RESPONSE_LEN + MAXNAMELEN)
+
+/* bitmask of supported algorithms */
+#if MSCHAP_SUPPORT
+#define MDTYPE_MICROSOFT_V2 0x1
+#define MDTYPE_MICROSOFT 0x2
+#endif /* MSCHAP_SUPPORT */
+#define MDTYPE_MD5 0x4
+#define MDTYPE_NONE 0
+
+#if MSCHAP_SUPPORT
+/* Return the digest alg. ID for the most preferred digest type. */
+#define CHAP_DIGEST(mdtype) \
+ ((mdtype) & MDTYPE_MD5)? CHAP_MD5: \
+ ((mdtype) & MDTYPE_MICROSOFT_V2)? CHAP_MICROSOFT_V2: \
+ ((mdtype) & MDTYPE_MICROSOFT)? CHAP_MICROSOFT: \
+ 0
+#else /* !MSCHAP_SUPPORT */
+#define CHAP_DIGEST(mdtype) \
+ ((mdtype) & MDTYPE_MD5)? CHAP_MD5: \
+ 0
+#endif /* MSCHAP_SUPPORT */
+
+/* Return the bit flag (lsb set) for our most preferred digest type. */
+#define CHAP_MDTYPE(mdtype) ((mdtype) ^ ((mdtype) - 1)) & (mdtype)
+
+/* Return the bit flag for a given digest algorithm ID. */
+#if MSCHAP_SUPPORT
+#define CHAP_MDTYPE_D(digest) \
+ ((digest) == CHAP_MICROSOFT_V2)? MDTYPE_MICROSOFT_V2: \
+ ((digest) == CHAP_MICROSOFT)? MDTYPE_MICROSOFT: \
+ ((digest) == CHAP_MD5)? MDTYPE_MD5: \
+ 0
+#else /* !MSCHAP_SUPPORT */
+#define CHAP_MDTYPE_D(digest) \
+ ((digest) == CHAP_MD5)? MDTYPE_MD5: \
+ 0
+#endif /* MSCHAP_SUPPORT */
+
+/* Can we do the requested digest? */
+#if MSCHAP_SUPPORT
+#define CHAP_CANDIGEST(mdtype, digest) \
+ ((digest) == CHAP_MICROSOFT_V2)? (mdtype) & MDTYPE_MICROSOFT_V2: \
+ ((digest) == CHAP_MICROSOFT)? (mdtype) & MDTYPE_MICROSOFT: \
+ ((digest) == CHAP_MD5)? (mdtype) & MDTYPE_MD5: \
+ 0
+#else /* !MSCHAP_SUPPORT */
+#define CHAP_CANDIGEST(mdtype, digest) \
+ ((digest) == CHAP_MD5)? (mdtype) & MDTYPE_MD5: \
+ 0
+#endif /* MSCHAP_SUPPORT */
+
+/*
+ * The code for each digest type has to supply one of these.
+ */
+struct chap_digest_type {
+ int code;
+
+#if PPP_SERVER
+ /*
+ * Note: challenge and response arguments below are formatted as
+ * a length byte followed by the actual challenge/response data.
+ */
+ void (*generate_challenge)(ppp_pcb *pcb, unsigned char *challenge);
+ int (*verify_response)(ppp_pcb *pcb, int id, const char *name,
+ const unsigned char *secret, int secret_len,
+ const unsigned char *challenge, const unsigned char *response,
+ char *message, int message_space);
+#endif /* PPP_SERVER */
+ void (*make_response)(ppp_pcb *pcb, unsigned char *response, int id, const char *our_name,
+ const unsigned char *challenge, const char *secret, int secret_len,
+ unsigned char *priv);
+ int (*check_success)(ppp_pcb *pcb, unsigned char *pkt, int len, unsigned char *priv);
+ void (*handle_failure)(ppp_pcb *pcb, unsigned char *pkt, int len);
+};
+
+/*
+ * Each interface is described by chap structure.
+ */
+#if CHAP_SUPPORT
+typedef struct chap_client_state {
+ u8_t flags;
+ const char *name;
+ const struct chap_digest_type *digest;
+ unsigned char priv[64]; /* private area for digest's use */
+} chap_client_state;
+
+#if PPP_SERVER
+typedef struct chap_server_state {
+ u8_t flags;
+ u8_t id;
+ const char *name;
+ const struct chap_digest_type *digest;
+ int challenge_xmits;
+ int challenge_pktlen;
+ unsigned char challenge[CHAL_MAX_PKTLEN];
+} chap_server_state;
+#endif /* PPP_SERVER */
+#endif /* CHAP_SUPPORT */
+
+#if 0 /* UNUSED */
+/* Hook for a plugin to validate CHAP challenge */
+extern int (*chap_verify_hook)(char *name, char *ourname, int id,
+ const struct chap_digest_type *digest,
+ unsigned char *challenge, unsigned char *response,
+ char *message, int message_space);
+#endif /* UNUSED */
+
+#if PPP_SERVER
+/* Called by authentication code to start authenticating the peer. */
+extern void chap_auth_peer(ppp_pcb *pcb, const char *our_name, int digest_code);
+#endif /* PPP_SERVER */
+
+/* Called by auth. code to start authenticating us to the peer. */
+extern void chap_auth_with_peer(ppp_pcb *pcb, const char *our_name, int digest_code);
+
+/* Represents the CHAP protocol to the main pppd code */
+extern const struct protent chap_protent;
+
+#endif /* CHAP_H */
+#endif /* PPP_SUPPORT && CHAP_SUPPORT */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/chap_ms.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/chap_ms.h
new file mode 100644
index 0000000..0795291
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/chap_ms.h
@@ -0,0 +1,44 @@
+/*
+ * chap_ms.h - Challenge Handshake Authentication Protocol definitions.
+ *
+ * Copyright (c) 1995 Eric Rosenquist. 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(s) of the authors of this software must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission.
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $Id: chap_ms.h,v 1.13 2004/11/15 22:13:26 paulus Exp $
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT && MSCHAP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#ifndef CHAPMS_INCLUDE
+#define CHAPMS_INCLUDE
+
+extern const struct chap_digest_type chapms_digest;
+extern const struct chap_digest_type chapms2_digest;
+
+#endif /* CHAPMS_INCLUDE */
+
+#endif /* PPP_SUPPORT && MSCHAP_SUPPORT */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/eap.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/eap.h
new file mode 100644
index 0000000..3ee9aaf
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/eap.h
@@ -0,0 +1,169 @@
+/*
+ * eap.h - Extensible Authentication Protocol for PPP (RFC 2284)
+ *
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ *
+ * Non-exclusive rights to redistribute, modify, translate, and use
+ * this software in source and binary forms, in whole or in part, is
+ * hereby granted, provided that the above copyright notice is
+ * duplicated in any source form, and that neither the name of the
+ * copyright holder nor the author is used to endorse or promote
+ * products derived from this software.
+ *
+ * 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.
+ *
+ * Original version by James Carlson
+ *
+ * $Id: eap.h,v 1.2 2003/06/11 23:56:26 paulus Exp $
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT && EAP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#ifndef PPP_EAP_H
+#define PPP_EAP_H
+
+#include "ppp.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Packet header = Code, id, length.
+ */
+#define EAP_HEADERLEN 4
+
+
+/* EAP message codes. */
+#define EAP_REQUEST 1
+#define EAP_RESPONSE 2
+#define EAP_SUCCESS 3
+#define EAP_FAILURE 4
+
+/* EAP types */
+#define EAPT_IDENTITY 1
+#define EAPT_NOTIFICATION 2
+#define EAPT_NAK 3 /* (response only) */
+#define EAPT_MD5CHAP 4
+#define EAPT_OTP 5 /* One-Time Password; RFC 1938 */
+#define EAPT_TOKEN 6 /* Generic Token Card */
+/* 7 and 8 are unassigned. */
+#define EAPT_RSA 9 /* RSA Public Key Authentication */
+#define EAPT_DSS 10 /* DSS Unilateral */
+#define EAPT_KEA 11 /* KEA */
+#define EAPT_KEA_VALIDATE 12 /* KEA-VALIDATE */
+#define EAPT_TLS 13 /* EAP-TLS */
+#define EAPT_DEFENDER 14 /* Defender Token (AXENT) */
+#define EAPT_W2K 15 /* Windows 2000 EAP */
+#define EAPT_ARCOT 16 /* Arcot Systems */
+#define EAPT_CISCOWIRELESS 17 /* Cisco Wireless */
+#define EAPT_NOKIACARD 18 /* Nokia IP smart card */
+#define EAPT_SRP 19 /* Secure Remote Password */
+/* 20 is deprecated */
+
+/* EAP SRP-SHA1 Subtypes */
+#define EAPSRP_CHALLENGE 1 /* Request 1 - Challenge */
+#define EAPSRP_CKEY 1 /* Response 1 - Client Key */
+#define EAPSRP_SKEY 2 /* Request 2 - Server Key */
+#define EAPSRP_CVALIDATOR 2 /* Response 2 - Client Validator */
+#define EAPSRP_SVALIDATOR 3 /* Request 3 - Server Validator */
+#define EAPSRP_ACK 3 /* Response 3 - final ack */
+#define EAPSRP_LWRECHALLENGE 4 /* Req/resp 4 - Lightweight rechal */
+
+#define SRPVAL_EBIT 0x00000001 /* Use shared key for ECP */
+
+#define SRP_PSEUDO_ID "pseudo_"
+#define SRP_PSEUDO_LEN 7
+
+#define MD5_SIGNATURE_SIZE 16
+#define EAP_MIN_CHALLENGE_LENGTH 17
+#define EAP_MAX_CHALLENGE_LENGTH 24
+#define EAP_MIN_MAX_POWER_OF_TWO_CHALLENGE_LENGTH 3 /* 2^3-1 = 7, 17+7 = 24 */
+
+#define EAP_STATES \
+ "Initial", "Pending", "Closed", "Listen", "Identify", \
+ "SRP1", "SRP2", "SRP3", "MD5Chall", "Open", "SRP4", "BadAuth"
+
+#define eap_client_active(pcb) ((pcb)->eap.es_client.ea_state == eapListen)
+#if PPP_SERVER
+#define eap_server_active(pcb) \
+ ((pcb)->eap.es_server.ea_state >= eapIdentify && \
+ (pcb)->eap.es_server.ea_state <= eapMD5Chall)
+#endif /* PPP_SERVER */
+
+/*
+ * Complete EAP state for one PPP session.
+ */
+enum eap_state_code {
+ eapInitial = 0, /* No EAP authentication yet requested */
+ eapPending, /* Waiting for LCP (no timer) */
+ eapClosed, /* Authentication not in use */
+ eapListen, /* Client ready (and timer running) */
+ eapIdentify, /* EAP Identify sent */
+ eapSRP1, /* Sent EAP SRP-SHA1 Subtype 1 */
+ eapSRP2, /* Sent EAP SRP-SHA1 Subtype 2 */
+ eapSRP3, /* Sent EAP SRP-SHA1 Subtype 3 */
+ eapMD5Chall, /* Sent MD5-Challenge */
+ eapOpen, /* Completed authentication */
+ eapSRP4, /* Sent EAP SRP-SHA1 Subtype 4 */
+ eapBadAuth /* Failed authentication */
+};
+
+struct eap_auth {
+ const char *ea_name; /* Our name */
+ char ea_peer[MAXNAMELEN +1]; /* Peer's name */
+ void *ea_session; /* Authentication library linkage */
+ u_char *ea_skey; /* Shared encryption key */
+ u_short ea_namelen; /* Length of our name */
+ u_short ea_peerlen; /* Length of peer's name */
+ enum eap_state_code ea_state;
+ u_char ea_id; /* Current id */
+ u_char ea_requests; /* Number of Requests sent/received */
+ u_char ea_responses; /* Number of Responses */
+ u_char ea_type; /* One of EAPT_* */
+ u32_t ea_keyflags; /* SRP shared key usage flags */
+};
+
+#ifndef EAP_MAX_CHALLENGE_LENGTH
+#define EAP_MAX_CHALLENGE_LENGTH 24
+#endif
+typedef struct eap_state {
+ struct eap_auth es_client; /* Client (authenticatee) data */
+#if PPP_SERVER
+ struct eap_auth es_server; /* Server (authenticator) data */
+#endif /* PPP_SERVER */
+ int es_savedtime; /* Saved timeout */
+ int es_rechallenge; /* EAP rechallenge interval */
+ int es_lwrechallenge; /* SRP lightweight rechallenge inter */
+ u8_t es_usepseudo; /* Use SRP Pseudonym if offered one */
+ int es_usedpseudo; /* Set if we already sent PN */
+ int es_challen; /* Length of challenge string */
+ u_char es_challenge[EAP_MAX_CHALLENGE_LENGTH];
+} eap_state;
+
+/*
+ * Timeouts.
+ */
+#if 0 /* moved to ppp_opts.h */
+#define EAP_DEFTIMEOUT 3 /* Timeout (seconds) for rexmit */
+#define EAP_DEFTRANSMITS 10 /* max # times to transmit */
+#define EAP_DEFREQTIME 20 /* Time to wait for peer request */
+#define EAP_DEFALLOWREQ 20 /* max # times to accept requests */
+#endif /* moved to ppp_opts.h */
+
+void eap_authwithpeer(ppp_pcb *pcb, const char *localname);
+void eap_authpeer(ppp_pcb *pcb, const char *localname);
+
+extern const struct protent eap_protent;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* PPP_EAP_H */
+
+#endif /* PPP_SUPPORT && EAP_SUPPORT */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/ecp.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/ecp.h
new file mode 100644
index 0000000..5cdce29
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/ecp.h
@@ -0,0 +1,50 @@
+/*
+ * ecp.h - Definitions for PPP Encryption Control Protocol.
+ *
+ * Copyright (c) 2002 Google, Inc.
+ * 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(s) of the authors of this software must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission.
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $Id: ecp.h,v 1.2 2003/01/10 07:12:36 fcusack Exp $
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT && ECP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+typedef struct ecp_options {
+ bool required; /* Is ECP required? */
+ unsigned enctype; /* Encryption type */
+} ecp_options;
+
+extern fsm ecp_fsm[];
+extern ecp_options ecp_wantoptions[];
+extern ecp_options ecp_gotoptions[];
+extern ecp_options ecp_allowoptions[];
+extern ecp_options ecp_hisoptions[];
+
+extern const struct protent ecp_protent;
+
+#endif /* PPP_SUPPORT && ECP_SUPPORT */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/eui64.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/eui64.h
new file mode 100644
index 0000000..20ac22e
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/eui64.h
@@ -0,0 +1,94 @@
+/*
+ * eui64.h - EUI64 routines for IPv6CP.
+ *
+ * Copyright (c) 1999 Tommi Komulainen. 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(s) of the authors of this software must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission.
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by Tommi Komulainen
+ * <Tommi.Komulainen@iki.fi>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $Id: eui64.h,v 1.6 2002/12/04 23:03:32 paulus Exp $
+*/
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT && PPP_IPV6_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#ifndef EUI64_H
+#define EUI64_H
+
+/*
+ * @todo:
+ *
+ * Maybe this should be done by processing struct in6_addr directly...
+ */
+typedef union
+{
+ u8_t e8[8];
+ u16_t e16[4];
+ u32_t e32[2];
+} eui64_t;
+
+#define eui64_iszero(e) (((e).e32[0] | (e).e32[1]) == 0)
+#define eui64_equals(e, o) (((e).e32[0] == (o).e32[0]) && \
+ ((e).e32[1] == (o).e32[1]))
+#define eui64_zero(e) (e).e32[0] = (e).e32[1] = 0;
+
+#define eui64_copy(s, d) memcpy(&(d), &(s), sizeof(eui64_t))
+
+#define eui64_magic(e) do { \
+ (e).e32[0] = magic(); \
+ (e).e32[1] = magic(); \
+ (e).e8[0] &= ~2; \
+ } while (0)
+#define eui64_magic_nz(x) do { \
+ eui64_magic(x); \
+ } while (eui64_iszero(x))
+#define eui64_magic_ne(x, y) do { \
+ eui64_magic(x); \
+ } while (eui64_equals(x, y))
+
+#define eui64_get(ll, cp) do { \
+ eui64_copy((*cp), (ll)); \
+ (cp) += sizeof(eui64_t); \
+ } while (0)
+
+#define eui64_put(ll, cp) do { \
+ eui64_copy((ll), (*cp)); \
+ (cp) += sizeof(eui64_t); \
+ } while (0)
+
+#define eui64_set32(e, l) do { \
+ (e).e32[0] = 0; \
+ (e).e32[1] = lwip_htonl(l); \
+ } while (0)
+#define eui64_setlo32(e, l) eui64_set32(e, l)
+
+char *eui64_ntoa(eui64_t); /* Returns ascii representation of id */
+
+#endif /* EUI64_H */
+#endif /* PPP_SUPPORT && PPP_IPV6_SUPPORT */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/fsm.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/fsm.h
new file mode 100644
index 0000000..b6915d3
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/fsm.h
@@ -0,0 +1,175 @@
+/*
+ * fsm.h - {Link, IP} Control Protocol Finite State Machine definitions.
+ *
+ * Copyright (c) 1984-2000 Carnegie Mellon University. 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 "Carnegie Mellon University" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission. For permission or any legal
+ * details, please contact
+ * Office of Technology Transfer
+ * Carnegie Mellon University
+ * 5000 Forbes Avenue
+ * Pittsburgh, PA 15213-3890
+ * (412) 268-4387, fax: (412) 268-7395
+ * tech-transfer@andrew.cmu.edu
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by Computing Services
+ * at Carnegie Mellon University (http://www.cmu.edu/computing/)."
+ *
+ * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
+ * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $Id: fsm.h,v 1.10 2004/11/13 02:28:15 paulus Exp $
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#ifndef FSM_H
+#define FSM_H
+
+#include "ppp.h"
+
+/*
+ * Packet header = Code, id, length.
+ */
+#define HEADERLEN 4
+
+
+/*
+ * 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 {
+ ppp_pcb *pcb; /* PPP Interface */
+ const struct fsm_callbacks *callbacks; /* Callback routines */
+ const char *term_reason; /* Reason for closing protocol */
+ u8_t seen_ack; /* Have received valid Ack/Nak/Rej to Req */
+ /* -- This is our only flag, we might use u_int :1 if we have more flags */
+ u16_t protocol; /* Data Link Layer Protocol field value */
+ u8_t state; /* State */
+ u8_t flags; /* Contains option bits */
+ u8_t id; /* Current id */
+ u8_t reqid; /* Current request id */
+ u8_t retransmits; /* Number of retransmissions left */
+ u8_t nakloops; /* Number of nak loops since last ack */
+ u8_t rnakloops; /* Number of naks received */
+ u8_t maxnakloops; /* Maximum number of nak loops tolerated
+ (necessary because IPCP require a custom large max nak loops value) */
+ u8_t term_reason_len; /* Length of term_reason */
+} fsm;
+
+
+typedef struct fsm_callbacks {
+ void (*resetci) /* Reset our Configuration Information */
+ (fsm *);
+ int (*cilen) /* Length of our Configuration Information */
+ (fsm *);
+ void (*addci) /* Add our Configuration Information */
+ (fsm *, u_char *, int *);
+ int (*ackci) /* ACK our Configuration Information */
+ (fsm *, u_char *, int);
+ int (*nakci) /* NAK our Configuration Information */
+ (fsm *, u_char *, int, int);
+ int (*rejci) /* Reject our Configuration Information */
+ (fsm *, u_char *, int);
+ int (*reqci) /* Request peer's Configuration Information */
+ (fsm *, u_char *, int *, int);
+ void (*up) /* Called when fsm reaches PPP_FSM_OPENED state */
+ (fsm *);
+ void (*down) /* Called when fsm leaves PPP_FSM_OPENED state */
+ (fsm *);
+ void (*starting) /* Called when we want the lower layer */
+ (fsm *);
+ void (*finished) /* Called when we don't want the lower layer */
+ (fsm *);
+ void (*protreject) /* Called when Protocol-Reject received */
+ (int);
+ void (*retransmit) /* Retransmission is necessary */
+ (fsm *);
+ int (*extcode) /* Called when unknown code received */
+ (fsm *, int, int, u_char *, int);
+ const char *proto_name; /* String name for protocol (for messages) */
+} fsm_callbacks;
+
+
+/*
+ * Link states.
+ */
+#define PPP_FSM_INITIAL 0 /* Down, hasn't been opened */
+#define PPP_FSM_STARTING 1 /* Down, been opened */
+#define PPP_FSM_CLOSED 2 /* Up, hasn't been opened */
+#define PPP_FSM_STOPPED 3 /* Open, waiting for down event */
+#define PPP_FSM_CLOSING 4 /* Terminating the connection, not open */
+#define PPP_FSM_STOPPING 5 /* Terminating, but open */
+#define PPP_FSM_REQSENT 6 /* We've sent a Config Request */
+#define PPP_FSM_ACKRCVD 7 /* We've received a Config Ack */
+#define PPP_FSM_ACKSENT 8 /* We've sent a Config Ack */
+#define PPP_FSM_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 */
+
+
+/*
+ * Timeouts.
+ */
+#if 0 /* moved to ppp_opts.h */
+#define DEFTIMEOUT 3 /* Timeout time in seconds */
+#define DEFMAXTERMREQS 2 /* Maximum Terminate-Request transmissions */
+#define DEFMAXCONFREQS 10 /* Maximum Configure-Request transmissions */
+#define DEFMAXNAKLOOPS 5 /* Maximum number of nak loops */
+#endif /* moved to ppp_opts.h */
+
+
+/*
+ * Prototypes
+ */
+void fsm_init(fsm *f);
+void fsm_lowerup(fsm *f);
+void fsm_lowerdown(fsm *f);
+void fsm_open(fsm *f);
+void fsm_close(fsm *f, const char *reason);
+void fsm_input(fsm *f, u_char *inpacket, int l);
+void fsm_protreject(fsm *f);
+void fsm_sdata(fsm *f, u_char code, u_char id, const u_char *data, int datalen);
+
+
+#endif /* FSM_H */
+#endif /* PPP_SUPPORT */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/ipcp.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/ipcp.h
new file mode 100644
index 0000000..45f46b3
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/ipcp.h
@@ -0,0 +1,126 @@
+/*
+ * ipcp.h - IP Control Protocol definitions.
+ *
+ * Copyright (c) 1984-2000 Carnegie Mellon University. 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 "Carnegie Mellon University" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission. For permission or any legal
+ * details, please contact
+ * Office of Technology Transfer
+ * Carnegie Mellon University
+ * 5000 Forbes Avenue
+ * Pittsburgh, PA 15213-3890
+ * (412) 268-4387, fax: (412) 268-7395
+ * tech-transfer@andrew.cmu.edu
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by Computing Services
+ * at Carnegie Mellon University (http://www.cmu.edu/computing/)."
+ *
+ * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
+ * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $Id: ipcp.h,v 1.14 2002/12/04 23:03:32 paulus Exp $
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT && PPP_IPV4_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#ifndef IPCP_H
+#define IPCP_H
+
+/*
+ * Options.
+ */
+#define CI_ADDRS 1 /* IP Addresses */
+#if VJ_SUPPORT
+#define CI_COMPRESSTYPE 2 /* Compression Type */
+#endif /* VJ_SUPPORT */
+#define CI_ADDR 3
+
+#if LWIP_DNS
+#define CI_MS_DNS1 129 /* Primary DNS value */
+#define CI_MS_DNS2 131 /* Secondary DNS value */
+#endif /* LWIP_DNS */
+#if 0 /* UNUSED - WINS */
+#define CI_MS_WINS1 130 /* Primary WINS value */
+#define CI_MS_WINS2 132 /* Secondary WINS value */
+#endif /* UNUSED - WINS */
+
+#if VJ_SUPPORT
+#define MAX_STATES 16 /* from slcompress.h */
+
+#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*/
+#endif /* VJ_SUPPORT */
+
+typedef struct ipcp_options {
+ unsigned int neg_addr :1; /* Negotiate IP Address? */
+ unsigned int old_addrs :1; /* Use old (IP-Addresses) option? */
+ unsigned int req_addr :1; /* Ask peer to send IP address? */
+#if 0 /* UNUSED */
+ unsigned int default_route :1; /* Assign default route through interface? */
+ unsigned int replace_default_route :1; /* Replace default route through interface? */
+#endif /* UNUSED */
+#if 0 /* UNUSED - PROXY ARP */
+ unsigned int proxy_arp :1; /* Make proxy ARP entry for peer? */
+#endif /* UNUSED - PROXY ARP */
+#if VJ_SUPPORT
+ unsigned int neg_vj :1; /* Van Jacobson Compression? */
+ unsigned int old_vj :1; /* use old (short) form of VJ option? */
+ unsigned int cflag :1;
+#endif /* VJ_SUPPORT */
+ unsigned int accept_local :1; /* accept peer's value for ouraddr */
+ unsigned int accept_remote :1; /* accept peer's value for hisaddr */
+#if LWIP_DNS
+ unsigned int req_dns1 :1; /* Ask peer to send primary DNS address? */
+ unsigned int req_dns2 :1; /* Ask peer to send secondary DNS address? */
+#endif /* LWIP_DNS */
+
+ u32_t ouraddr, hisaddr; /* Addresses in NETWORK BYTE ORDER */
+#if LWIP_DNS
+ u32_t dnsaddr[2]; /* Primary and secondary MS DNS entries */
+#endif /* LWIP_DNS */
+#if 0 /* UNUSED - WINS */
+ u32_t winsaddr[2]; /* Primary and secondary MS WINS entries */
+#endif /* UNUSED - WINS */
+
+#if VJ_SUPPORT
+ u16_t vj_protocol; /* protocol value to use in VJ option */
+ u8_t maxslotindex; /* values for RFC1332 VJ compression neg. */
+#endif /* VJ_SUPPORT */
+} ipcp_options;
+
+#if 0 /* UNUSED, already defined by lwIP */
+char *ip_ntoa (u32_t);
+#endif /* UNUSED, already defined by lwIP */
+
+extern const struct protent ipcp_protent;
+
+#endif /* IPCP_H */
+#endif /* PPP_SUPPORT && PPP_IPV4_SUPPORT */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/ipv6cp.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/ipv6cp.h
new file mode 100644
index 0000000..07d1ae3
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/ipv6cp.h
@@ -0,0 +1,183 @@
+/*
+ * ipv6cp.h - PPP IPV6 Control Protocol.
+ *
+ * Copyright (c) 1999 Tommi Komulainen. 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(s) of the authors of this software must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission.
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by Tommi Komulainen
+ * <Tommi.Komulainen@iki.fi>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+/* Original version, based on RFC2023 :
+
+ Copyright (c) 1995, 1996, 1997 Francis.Dupont@inria.fr, INRIA Rocquencourt,
+ Alain.Durand@imag.fr, IMAG,
+ Jean-Luc.Richier@imag.fr, IMAG-LSR.
+
+ Copyright (c) 1998, 1999 Francis.Dupont@inria.fr, GIE DYADE,
+ Alain.Durand@imag.fr, IMAG,
+ Jean-Luc.Richier@imag.fr, IMAG-LSR.
+
+ Ce travail a été fait au sein du GIE DYADE (Groupement d'Intérêt
+ Économique ayant pour membres BULL S.A. et l'INRIA).
+
+ Ce logiciel informatique est disponible aux conditions
+ usuelles dans la recherche, c'est-à-dire qu'il peut
+ être utilisé, copié, modifié, distribué à l'unique
+ condition que ce texte soit conservé afin que
+ l'origine de ce logiciel soit reconnue.
+
+ Le nom de l'Institut National de Recherche en Informatique
+ et en Automatique (INRIA), de l'IMAG, ou d'une personne morale
+ ou physique ayant participé à l'élaboration de ce logiciel ne peut
+ être utilisé sans son accord préalable explicite.
+
+ Ce logiciel est fourni tel quel sans aucune garantie,
+ support ou responsabilité d'aucune sorte.
+ Ce logiciel est dérivé de sources d'origine
+ "University of California at Berkeley" et
+ "Digital Equipment Corporation" couvertes par des copyrights.
+
+ L'Institut d'Informatique et de Mathématiques Appliquées de Grenoble (IMAG)
+ est une fédération d'unités mixtes de recherche du CNRS, de l'Institut National
+ Polytechnique de Grenoble et de l'Université Joseph Fourier regroupant
+ sept laboratoires dont le laboratoire Logiciels, Systèmes, Réseaux (LSR).
+
+ This work has been done in the context of GIE DYADE (joint R & D venture
+ between BULL S.A. and INRIA).
+
+ This software is available with usual "research" terms
+ with the aim of retain credits of the software.
+ Permission to use, copy, modify and distribute this software for any
+ purpose and without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies,
+ and the name of INRIA, IMAG, or any contributor not be used in advertising
+ or publicity pertaining to this material without the prior explicit
+ permission. The software is provided "as is" without any
+ warranties, support or liabilities of any kind.
+ This software is derived from source code from
+ "University of California at Berkeley" and
+ "Digital Equipment Corporation" protected by copyrights.
+
+ Grenoble's Institute of Computer Science and Applied Mathematics (IMAG)
+ is a federation of seven research units funded by the CNRS, National
+ Polytechnic Institute of Grenoble and University Joseph Fourier.
+ The research unit in Software, Systems, Networks (LSR) is member of IMAG.
+*/
+
+/*
+ * Derived from :
+ *
+ *
+ * ipcp.h - IP Control Protocol definitions.
+ *
+ * Copyright (c) 1984-2000 Carnegie Mellon University. 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 "Carnegie Mellon University" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission. For permission or any legal
+ * details, please contact
+ * Office of Technology Transfer
+ * Carnegie Mellon University
+ * 5000 Forbes Avenue
+ * Pittsburgh, PA 15213-3890
+ * (412) 268-4387, fax: (412) 268-7395
+ * tech-transfer@andrew.cmu.edu
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by Computing Services
+ * at Carnegie Mellon University (http://www.cmu.edu/computing/)."
+ *
+ * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
+ * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $Id: ipv6cp.h,v 1.7 2002/12/04 23:03:32 paulus Exp $
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT && PPP_IPV6_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#ifndef IPV6CP_H
+#define IPV6CP_H
+
+#include "eui64.h"
+
+/*
+ * Options.
+ */
+#define CI_IFACEID 1 /* Interface Identifier */
+#ifdef IPV6CP_COMP
+#define CI_COMPRESSTYPE 2 /* Compression Type */
+#endif /* IPV6CP_COMP */
+
+/* No compression types yet defined.
+ *#define IPV6CP_COMP 0x004f
+ */
+typedef struct ipv6cp_options {
+ unsigned int neg_ifaceid :1; /* Negotiate interface identifier? */
+ unsigned int req_ifaceid :1; /* Ask peer to send interface identifier? */
+ unsigned int accept_local :1; /* accept peer's value for iface id? */
+ unsigned int opt_local :1; /* ourtoken set by option */
+ unsigned int opt_remote :1; /* histoken set by option */
+ unsigned int use_ip :1; /* use IP as interface identifier */
+#if 0
+ unsigned int use_persistent :1; /* use uniquely persistent value for address */
+#endif
+#ifdef IPV6CP_COMP
+ unsigned int neg_vj :1; /* Van Jacobson Compression? */
+#endif /* IPV6CP_COMP */
+
+#ifdef IPV6CP_COMP
+ u_short vj_protocol; /* protocol value to use in VJ option */
+#endif /* IPV6CP_COMP */
+ eui64_t ourid, hisid; /* Interface identifiers */
+} ipv6cp_options;
+
+extern const struct protent ipv6cp_protent;
+
+#endif /* IPV6CP_H */
+#endif /* PPP_SUPPORT && PPP_IPV6_SUPPORT */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/lcp.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/lcp.h
new file mode 100644
index 0000000..12e2a05
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/lcp.h
@@ -0,0 +1,171 @@
+/*
+ * lcp.h - Link Control Protocol definitions.
+ *
+ * Copyright (c) 1984-2000 Carnegie Mellon University. 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 "Carnegie Mellon University" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission. For permission or any legal
+ * details, please contact
+ * Office of Technology Transfer
+ * Carnegie Mellon University
+ * 5000 Forbes Avenue
+ * Pittsburgh, PA 15213-3890
+ * (412) 268-4387, fax: (412) 268-7395
+ * tech-transfer@andrew.cmu.edu
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by Computing Services
+ * at Carnegie Mellon University (http://www.cmu.edu/computing/)."
+ *
+ * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
+ * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $Id: lcp.h,v 1.20 2004/11/14 22:53:42 carlsonj Exp $
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#ifndef LCP_H
+#define LCP_H
+
+#include "ppp.h"
+
+/*
+ * Options.
+ */
+#define CI_VENDOR 0 /* Vendor Specific */
+#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_FCSALTERN 9 /* FCS-Alternatives */
+#define CI_SDP 10 /* Self-Describing-Pad */
+#define CI_NUMBERED 11 /* Numbered-Mode */
+#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 */
+#define CI_MPPLUS 22 /* Multi-Link-Plus-Procedure */
+#define CI_LDISC 23 /* Link-Discriminator */
+#define CI_LCPAUTH 24 /* LCP Authentication */
+#define CI_COBS 25 /* Consistent Overhead Byte Stuffing */
+#define CI_PREFELIS 26 /* Prefix Elision */
+#define CI_MPHDRFMT 27 /* MP Header Format */
+#define CI_I18N 28 /* Internationalization */
+#define CI_SDL 29 /* Simple Data Link */
+
+/*
+ * 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 IDENTIF 12 /* Identification */
+#define TIMEREM 13 /* Time Remaining */
+
+/* Value used as data for CI_CALLBACK option */
+#define CBCP_OPT 6 /* Use callback control protocol */
+
+#if 0 /* moved to ppp_opts.h */
+#define DEFMRU 1500 /* Try for this */
+#define MINMRU 128 /* No MRUs below this */
+#define MAXMRU 16384 /* Normally limit MRU to this */
+#endif /* moved to ppp_opts.h */
+
+/* An endpoint discriminator, used with multilink. */
+#define MAX_ENDP_LEN 20 /* maximum length of discriminator value */
+struct epdisc {
+ unsigned char class_; /* -- The word "class" is reserved in C++. */
+ unsigned char length;
+ unsigned char value[MAX_ENDP_LEN];
+};
+
+/*
+ * The state of options is described by an lcp_options structure.
+ */
+typedef struct lcp_options {
+ unsigned int passive :1; /* Don't die if we don't get a response */
+ unsigned int silent :1; /* Wait for the other end to start first */
+#if 0 /* UNUSED */
+ unsigned int restart :1; /* Restart vs. exit after close */
+#endif /* UNUSED */
+ unsigned int neg_mru :1; /* Negotiate the MRU? */
+ unsigned int neg_asyncmap :1; /* Negotiate the async map? */
+#if PAP_SUPPORT
+ unsigned int neg_upap :1; /* Ask for UPAP authentication? */
+#endif /* PAP_SUPPORT */
+#if CHAP_SUPPORT
+ unsigned int neg_chap :1; /* Ask for CHAP authentication? */
+#endif /* CHAP_SUPPORT */
+#if EAP_SUPPORT
+ unsigned int neg_eap :1; /* Ask for EAP authentication? */
+#endif /* EAP_SUPPORT */
+ unsigned int neg_magicnumber :1; /* Ask for magic number? */
+ unsigned int neg_pcompression :1; /* HDLC Protocol Field Compression? */
+ unsigned int neg_accompression :1; /* HDLC Address/Control Field Compression? */
+#if LQR_SUPPORT
+ unsigned int neg_lqr :1; /* Negotiate use of Link Quality Reports */
+#endif /* LQR_SUPPORT */
+ unsigned int neg_cbcp :1; /* Negotiate use of CBCP */
+#ifdef HAVE_MULTILINK
+ unsigned int neg_mrru :1; /* negotiate multilink MRRU */
+#endif /* HAVE_MULTILINK */
+ unsigned int neg_ssnhf :1; /* negotiate short sequence numbers */
+ unsigned int neg_endpoint :1; /* negotiate endpoint discriminator */
+
+ u16_t mru; /* Value of MRU */
+#ifdef HAVE_MULTILINK
+ u16_t mrru; /* Value of MRRU, and multilink enable */
+#endif /* MULTILINK */
+#if CHAP_SUPPORT
+ u8_t chap_mdtype; /* which MD types (hashing algorithm) */
+#endif /* CHAP_SUPPORT */
+ u32_t asyncmap; /* Value of async map */
+ u32_t magicnumber;
+ u8_t numloops; /* Number of loops during magic number neg. */
+#if LQR_SUPPORT
+ u32_t lqr_period; /* Reporting period for LQR 1/100ths second */
+#endif /* LQR_SUPPORT */
+ struct epdisc endpoint; /* endpoint discriminator */
+} lcp_options;
+
+void lcp_open(ppp_pcb *pcb);
+void lcp_close(ppp_pcb *pcb, const char *reason);
+void lcp_lowerup(ppp_pcb *pcb);
+void lcp_lowerdown(ppp_pcb *pcb);
+void lcp_sprotrej(ppp_pcb *pcb, u_char *p, int len); /* send protocol reject */
+
+extern const struct protent lcp_protent;
+
+#if 0 /* moved to ppp_opts.h */
+/* Default number of times we receive our magic number from the peer
+ before deciding the link is looped-back. */
+#define DEFLOOPBACKFAIL 10
+#endif /* moved to ppp_opts.h */
+
+#endif /* LCP_H */
+#endif /* PPP_SUPPORT */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/magic.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/magic.h
new file mode 100644
index 0000000..a2a9b53
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/magic.h
@@ -0,0 +1,122 @@
+/*
+ * magic.h - PPP Magic Number definitions.
+ *
+ * Copyright (c) 1984-2000 Carnegie Mellon University. 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 "Carnegie Mellon University" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission. For permission or any legal
+ * details, please contact
+ * Office of Technology Transfer
+ * Carnegie Mellon University
+ * 5000 Forbes Avenue
+ * Pittsburgh, PA 15213-3890
+ * (412) 268-4387, fax: (412) 268-7395
+ * tech-transfer@andrew.cmu.edu
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by Computing Services
+ * at Carnegie Mellon University (http://www.cmu.edu/computing/)."
+ *
+ * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
+ * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $Id: magic.h,v 1.5 2003/06/11 23:56:26 paulus Exp $
+ */
+/*****************************************************************************
+* 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.
+*****************************************************************************/
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#ifndef MAGIC_H
+#define MAGIC_H
+
+/***********************
+*** PUBLIC FUNCTIONS ***
+***********************/
+
+/*
+ * Initialize the random number generator.
+ */
+void magic_init(void);
+
+/*
+ * Randomize our random seed value. To be called for truely random events
+ * such as user operations and network traffic.
+ */
+void magic_randomize(void);
+
+/*
+ * Return a new random number.
+ */
+u32_t magic(void); /* Returns the next magic number */
+
+/*
+ * Fill buffer with random bytes
+ *
+ * Use the random pool to generate random data. This degrades to pseudo
+ * random when used faster than randomness is supplied using magic_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 magic_random_bytes(unsigned char *buf, u32_t buf_len);
+
+/*
+ * Return a new random number between 0 and (2^pow)-1 included.
+ */
+u32_t magic_pow(u8_t pow);
+
+#endif /* MAGIC_H */
+
+#endif /* PPP_SUPPORT */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/mppe.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/mppe.h
new file mode 100644
index 0000000..1ae8a5d
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/mppe.h
@@ -0,0 +1,173 @@
+/*
+ * mppe.h - Definitions for MPPE
+ *
+ * Copyright (c) 2008 Paul Mackerras. 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(s) of the authors of this software must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission.
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by Paul Mackerras
+ * <paulus@samba.org>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT && MPPE_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#ifndef MPPE_H
+#define MPPE_H
+
+#include "netif/ppp/pppcrypt.h"
+
+#define MPPE_PAD 4 /* MPPE growth per frame */
+#define MPPE_MAX_KEY_LEN 16 /* largest key length (128-bit) */
+
+/* option bits for ccp_options.mppe */
+#define MPPE_OPT_40 0x01 /* 40 bit */
+#define MPPE_OPT_128 0x02 /* 128 bit */
+#define MPPE_OPT_STATEFUL 0x04 /* stateful mode */
+/* unsupported opts */
+#define MPPE_OPT_56 0x08 /* 56 bit */
+#define MPPE_OPT_MPPC 0x10 /* MPPC compression */
+#define MPPE_OPT_D 0x20 /* Unknown */
+#define MPPE_OPT_UNSUPPORTED (MPPE_OPT_56|MPPE_OPT_MPPC|MPPE_OPT_D)
+#define MPPE_OPT_UNKNOWN 0x40 /* Bits !defined in RFC 3078 were set */
+
+/*
+ * This is not nice ... the alternative is a bitfield struct though.
+ * And unfortunately, we cannot share the same bits for the option
+ * names above since C and H are the same bit. We could do a u_int32
+ * but then we have to do a lwip_htonl() all the time and/or we still need
+ * to know which octet is which.
+ */
+#define MPPE_C_BIT 0x01 /* MPPC */
+#define MPPE_D_BIT 0x10 /* Obsolete, usage unknown */
+#define MPPE_L_BIT 0x20 /* 40-bit */
+#define MPPE_S_BIT 0x40 /* 128-bit */
+#define MPPE_M_BIT 0x80 /* 56-bit, not supported */
+#define MPPE_H_BIT 0x01 /* Stateless (in a different byte) */
+
+/* Does not include H bit; used for least significant octet only. */
+#define MPPE_ALL_BITS (MPPE_D_BIT|MPPE_L_BIT|MPPE_S_BIT|MPPE_M_BIT|MPPE_H_BIT)
+
+/* Build a CI from mppe opts (see RFC 3078) */
+#define MPPE_OPTS_TO_CI(opts, ci) \
+ do { \
+ u_char *ptr = ci; /* u_char[4] */ \
+ \
+ /* H bit */ \
+ if (opts & MPPE_OPT_STATEFUL) \
+ *ptr++ = 0x0; \
+ else \
+ *ptr++ = MPPE_H_BIT; \
+ *ptr++ = 0; \
+ *ptr++ = 0; \
+ \
+ /* S,L bits */ \
+ *ptr = 0; \
+ if (opts & MPPE_OPT_128) \
+ *ptr |= MPPE_S_BIT; \
+ if (opts & MPPE_OPT_40) \
+ *ptr |= MPPE_L_BIT; \
+ /* M,D,C bits not supported */ \
+ } while (/* CONSTCOND */ 0)
+
+/* The reverse of the above */
+#define MPPE_CI_TO_OPTS(ci, opts) \
+ do { \
+ const u_char *ptr = ci; /* u_char[4] */ \
+ \
+ opts = 0; \
+ \
+ /* H bit */ \
+ if (!(ptr[0] & MPPE_H_BIT)) \
+ opts |= MPPE_OPT_STATEFUL; \
+ \
+ /* S,L bits */ \
+ if (ptr[3] & MPPE_S_BIT) \
+ opts |= MPPE_OPT_128; \
+ if (ptr[3] & MPPE_L_BIT) \
+ opts |= MPPE_OPT_40; \
+ \
+ /* M,D,C bits */ \
+ if (ptr[3] & MPPE_M_BIT) \
+ opts |= MPPE_OPT_56; \
+ if (ptr[3] & MPPE_D_BIT) \
+ opts |= MPPE_OPT_D; \
+ if (ptr[3] & MPPE_C_BIT) \
+ opts |= MPPE_OPT_MPPC; \
+ \
+ /* Other bits */ \
+ if (ptr[0] & ~MPPE_H_BIT) \
+ opts |= MPPE_OPT_UNKNOWN; \
+ if (ptr[1] || ptr[2]) \
+ opts |= MPPE_OPT_UNKNOWN; \
+ if (ptr[3] & ~MPPE_ALL_BITS) \
+ opts |= MPPE_OPT_UNKNOWN; \
+ } while (/* CONSTCOND */ 0)
+
+/* Shared MPPE padding between MSCHAP and MPPE */
+#define SHA1_PAD_SIZE 40
+
+static const u8_t mppe_sha1_pad1[SHA1_PAD_SIZE] = {
+ 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
+};
+static const u8_t mppe_sha1_pad2[SHA1_PAD_SIZE] = {
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2
+};
+
+/*
+ * State for an MPPE (de)compressor.
+ */
+typedef struct ppp_mppe_state {
+ lwip_arc4_context arc4;
+ u8_t master_key[MPPE_MAX_KEY_LEN];
+ u8_t session_key[MPPE_MAX_KEY_LEN];
+ u8_t keylen; /* key length in bytes */
+ /* NB: 128-bit == 16, 40-bit == 8!
+ * If we want to support 56-bit, the unit has to change to bits
+ */
+ u8_t bits; /* MPPE control bits */
+ u16_t ccount; /* 12-bit coherency count (seqno) */
+ u16_t sanity_errors; /* take down LCP if too many */
+ unsigned int stateful :1; /* stateful mode flag */
+ unsigned int discard :1; /* stateful mode packet loss flag */
+} ppp_mppe_state;
+
+void mppe_set_key(ppp_pcb *pcb, ppp_mppe_state *state, u8_t *key);
+void mppe_init(ppp_pcb *pcb, ppp_mppe_state *state, u8_t options);
+void mppe_comp_reset(ppp_pcb *pcb, ppp_mppe_state *state);
+err_t mppe_compress(ppp_pcb *pcb, ppp_mppe_state *state, struct pbuf **pb, u16_t protocol);
+void mppe_decomp_reset(ppp_pcb *pcb, ppp_mppe_state *state);
+err_t mppe_decompress(ppp_pcb *pcb, ppp_mppe_state *state, struct pbuf **pb);
+
+#endif /* MPPE_H */
+#endif /* PPP_SUPPORT && MPPE_SUPPORT */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/polarssl/arc4.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/polarssl/arc4.h
new file mode 100644
index 0000000..4af724c
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/polarssl/arc4.h
@@ -0,0 +1,81 @@
+/**
+ * \file arc4.h
+ *
+ * Based on XySSL: Copyright (C) 2006-2008 Christophe Devine
+ *
+ * Copyright (C) 2009 Paul Bakker <polarssl_maintainer at polarssl dot org>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the names of PolarSSL or XySSL 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 COPYRIGHT HOLDERS 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 COPYRIGHT
+ * OWNER 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 "netif/ppp/ppp_opts.h"
+#if LWIP_INCLUDED_POLARSSL_ARC4
+
+#ifndef LWIP_INCLUDED_POLARSSL_ARC4_H
+#define LWIP_INCLUDED_POLARSSL_ARC4_H
+
+/**
+ * \brief ARC4 context structure
+ */
+typedef struct
+{
+ int x; /*!< permutation index */
+ int y; /*!< permutation index */
+ unsigned char m[256]; /*!< permutation table */
+}
+arc4_context;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief ARC4 key schedule
+ *
+ * \param ctx ARC4 context to be initialized
+ * \param key the secret key
+ * \param keylen length of the key
+ */
+void arc4_setup( arc4_context *ctx, unsigned char *key, int keylen );
+
+/**
+ * \brief ARC4 cipher function
+ *
+ * \param ctx ARC4 context
+ * \param buf buffer to be processed
+ * \param buflen amount of data in buf
+ */
+void arc4_crypt( arc4_context *ctx, unsigned char *buf, int buflen );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_INCLUDED_POLARSSL_ARC4_H */
+
+#endif /* LWIP_INCLUDED_POLARSSL_ARC4 */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/polarssl/des.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/polarssl/des.h
new file mode 100644
index 0000000..e893890
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/polarssl/des.h
@@ -0,0 +1,92 @@
+/**
+ * \file des.h
+ *
+ * Based on XySSL: Copyright (C) 2006-2008 Christophe Devine
+ *
+ * Copyright (C) 2009 Paul Bakker <polarssl_maintainer at polarssl dot org>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the names of PolarSSL or XySSL 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 COPYRIGHT HOLDERS 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 COPYRIGHT
+ * OWNER 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 "netif/ppp/ppp_opts.h"
+#if LWIP_INCLUDED_POLARSSL_DES
+
+#ifndef LWIP_INCLUDED_POLARSSL_DES_H
+#define LWIP_INCLUDED_POLARSSL_DES_H
+
+#define DES_ENCRYPT 1
+#define DES_DECRYPT 0
+
+/**
+ * \brief DES context structure
+ */
+typedef struct
+{
+ int mode; /*!< encrypt/decrypt */
+ unsigned long sk[32]; /*!< DES subkeys */
+}
+des_context;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief DES key schedule (56-bit, encryption)
+ *
+ * \param ctx DES context to be initialized
+ * \param key 8-byte secret key
+ */
+void des_setkey_enc( des_context *ctx, unsigned char key[8] );
+
+/**
+ * \brief DES key schedule (56-bit, decryption)
+ *
+ * \param ctx DES context to be initialized
+ * \param key 8-byte secret key
+ */
+void des_setkey_dec( des_context *ctx, unsigned char key[8] );
+
+/**
+ * \brief DES-ECB block encryption/decryption
+ *
+ * \param ctx DES context
+ * \param input 64-bit input block
+ * \param output 64-bit output block
+ */
+void des_crypt_ecb( des_context *ctx,
+ const unsigned char input[8],
+ unsigned char output[8] );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_INCLUDED_POLARSSL_DES_H */
+
+#endif /* LWIP_INCLUDED_POLARSSL_DES */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/polarssl/md4.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/polarssl/md4.h
new file mode 100644
index 0000000..5704456
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/polarssl/md4.h
@@ -0,0 +1,97 @@
+/**
+ * \file md4.h
+ *
+ * Based on XySSL: Copyright (C) 2006-2008 Christophe Devine
+ *
+ * Copyright (C) 2009 Paul Bakker <polarssl_maintainer at polarssl dot org>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the names of PolarSSL or XySSL 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 COPYRIGHT HOLDERS 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 COPYRIGHT
+ * OWNER 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 "netif/ppp/ppp_opts.h"
+#if LWIP_INCLUDED_POLARSSL_MD4
+
+#ifndef LWIP_INCLUDED_POLARSSL_MD4_H
+#define LWIP_INCLUDED_POLARSSL_MD4_H
+
+/**
+ * \brief MD4 context structure
+ */
+typedef struct
+{
+ unsigned long total[2]; /*!< number of bytes processed */
+ unsigned long state[4]; /*!< intermediate digest state */
+ unsigned char buffer[64]; /*!< data block being processed */
+}
+md4_context;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief MD4 context setup
+ *
+ * \param ctx context to be initialized
+ */
+void md4_starts( md4_context *ctx );
+
+/**
+ * \brief MD4 process buffer
+ *
+ * \param ctx MD4 context
+ * \param input buffer holding the data
+ * \param ilen length of the input data
+ */
+void md4_update( md4_context *ctx, const unsigned char *input, int ilen );
+
+/**
+ * \brief MD4 final digest
+ *
+ * \param ctx MD4 context
+ * \param output MD4 checksum result
+ */
+void md4_finish( md4_context *ctx, unsigned char output[16] );
+
+/**
+ * \brief Output = MD4( input buffer )
+ *
+ * \param input buffer holding the data
+ * \param ilen length of the input data
+ * \param output MD4 checksum result
+ */
+void md4( unsigned char *input, int ilen, unsigned char output[16] );
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_INCLUDED_POLARSSL_MD4_H */
+
+#endif /* LWIP_INCLUDED_POLARSSL_MD4 */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/polarssl/md5.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/polarssl/md5.h
new file mode 100644
index 0000000..1244011
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/polarssl/md5.h
@@ -0,0 +1,96 @@
+/**
+ * \file md5.h
+ *
+ * Based on XySSL: Copyright (C) 2006-2008 Christophe Devine
+ *
+ * Copyright (C) 2009 Paul Bakker <polarssl_maintainer at polarssl dot org>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the names of PolarSSL or XySSL 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 COPYRIGHT HOLDERS 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 COPYRIGHT
+ * OWNER 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 "netif/ppp/ppp_opts.h"
+#if LWIP_INCLUDED_POLARSSL_MD5
+
+#ifndef LWIP_INCLUDED_POLARSSL_MD5_H
+#define LWIP_INCLUDED_POLARSSL_MD5_H
+
+/**
+ * \brief MD5 context structure
+ */
+typedef struct
+{
+ unsigned long total[2]; /*!< number of bytes processed */
+ unsigned long state[4]; /*!< intermediate digest state */
+ unsigned char buffer[64]; /*!< data block being processed */
+}
+md5_context;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief MD5 context setup
+ *
+ * \param ctx context to be initialized
+ */
+void md5_starts( md5_context *ctx );
+
+/**
+ * \brief MD5 process buffer
+ *
+ * \param ctx MD5 context
+ * \param input buffer holding the data
+ * \param ilen length of the input data
+ */
+void md5_update( md5_context *ctx, const unsigned char *input, int ilen );
+
+/**
+ * \brief MD5 final digest
+ *
+ * \param ctx MD5 context
+ * \param output MD5 checksum result
+ */
+void md5_finish( md5_context *ctx, unsigned char output[16] );
+
+/**
+ * \brief Output = MD5( input buffer )
+ *
+ * \param input buffer holding the data
+ * \param ilen length of the input data
+ * \param output MD5 checksum result
+ */
+void md5( unsigned char *input, int ilen, unsigned char output[16] );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_INCLUDED_POLARSSL_MD5_H */
+
+#endif /* LWIP_INCLUDED_POLARSSL_MD5 */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/polarssl/sha1.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/polarssl/sha1.h
new file mode 100644
index 0000000..a4c53e0
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/polarssl/sha1.h
@@ -0,0 +1,96 @@
+/**
+ * \file sha1.h
+ *
+ * Based on XySSL: Copyright (C) 2006-2008 Christophe Devine
+ *
+ * Copyright (C) 2009 Paul Bakker <polarssl_maintainer at polarssl dot org>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the names of PolarSSL or XySSL 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 COPYRIGHT HOLDERS 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 COPYRIGHT
+ * OWNER 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 "netif/ppp/ppp_opts.h"
+#if LWIP_INCLUDED_POLARSSL_SHA1
+
+#ifndef LWIP_INCLUDED_POLARSSL_SHA1_H
+#define LWIP_INCLUDED_POLARSSL_SHA1_H
+
+/**
+ * \brief SHA-1 context structure
+ */
+typedef struct
+{
+ unsigned long total[2]; /*!< number of bytes processed */
+ unsigned long state[5]; /*!< intermediate digest state */
+ unsigned char buffer[64]; /*!< data block being processed */
+}
+sha1_context;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief SHA-1 context setup
+ *
+ * \param ctx context to be initialized
+ */
+void sha1_starts( sha1_context *ctx );
+
+/**
+ * \brief SHA-1 process buffer
+ *
+ * \param ctx SHA-1 context
+ * \param input buffer holding the data
+ * \param ilen length of the input data
+ */
+void sha1_update( sha1_context *ctx, const unsigned char *input, int ilen );
+
+/**
+ * \brief SHA-1 final digest
+ *
+ * \param ctx SHA-1 context
+ * \param output SHA-1 checksum result
+ */
+void sha1_finish( sha1_context *ctx, unsigned char output[20] );
+
+/**
+ * \brief Output = SHA-1( input buffer )
+ *
+ * \param input buffer holding the data
+ * \param ilen length of the input data
+ * \param output SHA-1 checksum result
+ */
+void sha1( unsigned char *input, int ilen, unsigned char output[20] );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_INCLUDED_POLARSSL_SHA1_H */
+
+#endif /* LWIP_INCLUDED_POLARSSL_SHA1 */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/ppp.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/ppp.h
new file mode 100644
index 0000000..d9ea097
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/ppp.h
@@ -0,0 +1,690 @@
+/*****************************************************************************
+* 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.
+*****************************************************************************/
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#ifndef PPP_H
+#define PPP_H
+
+#include "lwip/def.h"
+#include "lwip/stats.h"
+#include "lwip/mem.h"
+#include "lwip/netif.h"
+#include "lwip/sys.h"
+#include "lwip/timeouts.h"
+#if PPP_IPV6_SUPPORT
+#include "lwip/ip6_addr.h"
+#endif /* PPP_IPV6_SUPPORT */
+
+/* Disable non-working or rarely used PPP feature, so rarely that we don't want to bloat ppp_opts.h with them */
+#ifndef PPP_OPTIONS
+#define PPP_OPTIONS 0
+#endif
+
+#ifndef PPP_NOTIFY
+#define PPP_NOTIFY 0
+#endif
+
+#ifndef PPP_REMOTENAME
+#define PPP_REMOTENAME 0
+#endif
+
+#ifndef PPP_IDLETIMELIMIT
+#define PPP_IDLETIMELIMIT 0
+#endif
+
+#ifndef PPP_LCP_ADAPTIVE
+#define PPP_LCP_ADAPTIVE 0
+#endif
+
+#ifndef PPP_MAXCONNECT
+#define PPP_MAXCONNECT 0
+#endif
+
+#ifndef PPP_ALLOWED_ADDRS
+#define PPP_ALLOWED_ADDRS 0
+#endif
+
+#ifndef PPP_PROTOCOLNAME
+#define PPP_PROTOCOLNAME 0
+#endif
+
+#ifndef PPP_STATS_SUPPORT
+#define PPP_STATS_SUPPORT 0
+#endif
+
+#ifndef DEFLATE_SUPPORT
+#define DEFLATE_SUPPORT 0
+#endif
+
+#ifndef BSDCOMPRESS_SUPPORT
+#define BSDCOMPRESS_SUPPORT 0
+#endif
+
+#ifndef PREDICTOR_SUPPORT
+#define PREDICTOR_SUPPORT 0
+#endif
+
+/*************************
+*** PUBLIC DEFINITIONS ***
+*************************/
+
+/*
+ * The basic PPP frame.
+ */
+#define PPP_HDRLEN 4 /* octets for standard ppp header */
+#define PPP_FCSLEN 2 /* octets for FCS */
+
+/*
+ * Values for phase.
+ */
+#define PPP_PHASE_DEAD 0
+#define PPP_PHASE_MASTER 1
+#define PPP_PHASE_HOLDOFF 2
+#define PPP_PHASE_INITIALIZE 3
+#define PPP_PHASE_SERIALCONN 4
+#define PPP_PHASE_DORMANT 5
+#define PPP_PHASE_ESTABLISH 6
+#define PPP_PHASE_AUTHENTICATE 7
+#define PPP_PHASE_CALLBACK 8
+#define PPP_PHASE_NETWORK 9
+#define PPP_PHASE_RUNNING 10
+#define PPP_PHASE_TERMINATE 11
+#define PPP_PHASE_DISCONNECT 12
+
+/* 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. */
+#define PPPERR_PEERDEAD 9 /* Connection timeout */
+#define PPPERR_IDLETIMEOUT 10 /* Idle Timeout */
+#define PPPERR_CONNECTTIME 11 /* Max connect time reached */
+#define PPPERR_LOOPBACK 12 /* Loopback detected */
+
+/* Whether auth support is enabled at all */
+#define PPP_AUTH_SUPPORT (PAP_SUPPORT || CHAP_SUPPORT || EAP_SUPPORT)
+
+/************************
+*** PUBLIC DATA TYPES ***
+************************/
+
+/*
+ * Other headers require ppp_pcb definition for prototypes, but ppp_pcb
+ * require some structure definition from other headers as well, we are
+ * fixing the dependency loop here by declaring the ppp_pcb type then
+ * by including headers containing necessary struct definition for ppp_pcb
+ */
+typedef struct ppp_pcb_s ppp_pcb;
+
+/* Type definitions for BSD code. */
+#ifndef __u_char_defined
+typedef unsigned long u_long;
+typedef unsigned int u_int;
+typedef unsigned short u_short;
+typedef unsigned char u_char;
+#endif
+
+#include "fsm.h"
+#include "lcp.h"
+#if CCP_SUPPORT
+#include "ccp.h"
+#endif /* CCP_SUPPORT */
+#if MPPE_SUPPORT
+#include "mppe.h"
+#endif /* MPPE_SUPPORT */
+#if PPP_IPV4_SUPPORT
+#include "ipcp.h"
+#endif /* PPP_IPV4_SUPPORT */
+#if PPP_IPV6_SUPPORT
+#include "ipv6cp.h"
+#endif /* PPP_IPV6_SUPPORT */
+#if PAP_SUPPORT
+#include "upap.h"
+#endif /* PAP_SUPPORT */
+#if CHAP_SUPPORT
+#include "chap-new.h"
+#endif /* CHAP_SUPPORT */
+#if EAP_SUPPORT
+#include "eap.h"
+#endif /* EAP_SUPPORT */
+#if VJ_SUPPORT
+#include "vj.h"
+#endif /* VJ_SUPPORT */
+
+/* Link status callback function prototype */
+typedef void (*ppp_link_status_cb_fn)(ppp_pcb *pcb, int err_code, void *ctx);
+
+/*
+ * PPP configuration.
+ */
+typedef struct ppp_settings_s {
+
+#if PPP_SERVER && PPP_AUTH_SUPPORT
+ unsigned int auth_required :1; /* Peer is required to authenticate */
+ unsigned int null_login :1; /* Username of "" and a password of "" are acceptable */
+#endif /* PPP_SERVER && PPP_AUTH_SUPPORT */
+#if PPP_REMOTENAME
+ unsigned int explicit_remote :1; /* remote_name specified with remotename opt */
+#endif /* PPP_REMOTENAME */
+#if PAP_SUPPORT
+ unsigned int refuse_pap :1; /* Don't proceed auth. with PAP */
+#endif /* PAP_SUPPORT */
+#if CHAP_SUPPORT
+ unsigned int refuse_chap :1; /* Don't proceed auth. with CHAP */
+#endif /* CHAP_SUPPORT */
+#if MSCHAP_SUPPORT
+ unsigned int refuse_mschap :1; /* Don't proceed auth. with MS-CHAP */
+ unsigned int refuse_mschap_v2 :1; /* Don't proceed auth. with MS-CHAPv2 */
+#endif /* MSCHAP_SUPPORT */
+#if EAP_SUPPORT
+ unsigned int refuse_eap :1; /* Don't proceed auth. with EAP */
+#endif /* EAP_SUPPORT */
+#if LWIP_DNS
+ unsigned int usepeerdns :1; /* Ask peer for DNS adds */
+#endif /* LWIP_DNS */
+ unsigned int persist :1; /* Persist mode, always try to open the connection */
+#if PRINTPKT_SUPPORT
+ unsigned int hide_password :1; /* Hide password in dumped packets */
+#endif /* PRINTPKT_SUPPORT */
+ unsigned int noremoteip :1; /* Let him have no IP address */
+ unsigned int lax_recv :1; /* accept control chars in asyncmap */
+ unsigned int noendpoint :1; /* don't send/accept endpoint discriminator */
+#if PPP_LCP_ADAPTIVE
+ unsigned int lcp_echo_adaptive :1; /* request echo only if the link was idle */
+#endif /* PPP_LCP_ADAPTIVE */
+#if MPPE_SUPPORT
+ unsigned int require_mppe :1; /* Require MPPE (Microsoft Point to Point Encryption) */
+ unsigned int refuse_mppe_40 :1; /* Allow MPPE 40-bit mode? */
+ unsigned int refuse_mppe_128 :1; /* Allow MPPE 128-bit mode? */
+ unsigned int refuse_mppe_stateful :1; /* Allow MPPE stateful mode? */
+#endif /* MPPE_SUPPORT */
+
+ u16_t listen_time; /* time to listen first (ms), waiting for peer to send LCP packet */
+
+#if PPP_IDLETIMELIMIT
+ u16_t idle_time_limit; /* Disconnect if idle for this many seconds */
+#endif /* PPP_IDLETIMELIMIT */
+#if PPP_MAXCONNECT
+ u32_t maxconnect; /* Maximum connect time (seconds) */
+#endif /* PPP_MAXCONNECT */
+
+#if PPP_AUTH_SUPPORT
+ /* auth data */
+ const char *user; /* Username for PAP */
+ const char *passwd; /* Password for PAP, secret for CHAP */
+#if PPP_REMOTENAME
+ char remote_name[MAXNAMELEN + 1]; /* Peer's name for authentication */
+#endif /* PPP_REMOTENAME */
+
+#if PAP_SUPPORT
+ u8_t pap_timeout_time; /* Timeout (seconds) for auth-req retrans. */
+ u8_t pap_max_transmits; /* Number of auth-reqs sent */
+#if PPP_SERVER
+ u8_t pap_req_timeout; /* Time to wait for auth-req from peer */
+#endif /* PPP_SERVER */
+#endif /* PAP_SUPPPORT */
+
+#if CHAP_SUPPORT
+ u8_t chap_timeout_time; /* Timeout (seconds) for retransmitting req */
+ u8_t chap_max_transmits; /* max # times to send challenge */
+#if PPP_SERVER
+ u8_t chap_rechallenge_time; /* Time to wait for auth-req from peer */
+#endif /* PPP_SERVER */
+#endif /* CHAP_SUPPPORT */
+
+#if EAP_SUPPORT
+ u8_t eap_req_time; /* Time to wait (for retransmit/fail) */
+ u8_t eap_allow_req; /* Max Requests allowed */
+#if PPP_SERVER
+ u8_t eap_timeout_time; /* Time to wait (for retransmit/fail) */
+ u8_t eap_max_transmits; /* Max Requests allowed */
+#endif /* PPP_SERVER */
+#endif /* EAP_SUPPORT */
+
+#endif /* PPP_AUTH_SUPPORT */
+
+ u8_t fsm_timeout_time; /* Timeout time in seconds */
+ u8_t fsm_max_conf_req_transmits; /* Maximum Configure-Request transmissions */
+ u8_t fsm_max_term_transmits; /* Maximum Terminate-Request transmissions */
+ u8_t fsm_max_nak_loops; /* Maximum number of nak loops tolerated */
+
+ u8_t lcp_loopbackfail; /* Number of times we receive our magic number from the peer
+ before deciding the link is looped-back. */
+ u8_t lcp_echo_interval; /* Interval between LCP echo-requests */
+ u8_t lcp_echo_fails; /* Tolerance to unanswered echo-requests */
+
+} ppp_settings;
+
+#if PPP_SERVER
+struct ppp_addrs {
+#if PPP_IPV4_SUPPORT
+ ip4_addr_t our_ipaddr, his_ipaddr, netmask;
+#if LWIP_DNS
+ ip4_addr_t dns1, dns2;
+#endif /* LWIP_DNS */
+#endif /* PPP_IPV4_SUPPORT */
+#if PPP_IPV6_SUPPORT
+ ip6_addr_t our6_ipaddr, his6_ipaddr;
+#endif /* PPP_IPV6_SUPPORT */
+};
+#endif /* PPP_SERVER */
+
+/*
+ * PPP interface control block.
+ */
+struct ppp_pcb_s {
+ ppp_settings settings;
+ const struct link_callbacks *link_cb;
+ void *link_ctx_cb;
+ void (*link_status_cb)(ppp_pcb *pcb, int err_code, void *ctx); /* Status change callback */
+#if PPP_NOTIFY_PHASE
+ void (*notify_phase_cb)(ppp_pcb *pcb, u8_t phase, void *ctx); /* Notify phase callback */
+#endif /* PPP_NOTIFY_PHASE */
+ void *ctx_cb; /* Callbacks optional pointer */
+ struct netif *netif; /* PPP interface */
+ u8_t phase; /* where the link is at */
+ u8_t err_code; /* Code indicating why interface is down. */
+
+ /* flags */
+#if PPP_IPV4_SUPPORT
+ unsigned int ask_for_local :1; /* request our address from peer */
+ unsigned int ipcp_is_open :1; /* haven't called np_finished() */
+ unsigned int ipcp_is_up :1; /* have called ipcp_up() */
+ unsigned int if4_up :1; /* True when the IPv4 interface is up. */
+#if 0 /* UNUSED - PROXY ARP */
+ unsigned int proxy_arp_set :1; /* Have created proxy arp entry */
+#endif /* UNUSED - PROXY ARP */
+#endif /* PPP_IPV4_SUPPORT */
+#if PPP_IPV6_SUPPORT
+ unsigned int ipv6cp_is_up :1; /* have called ip6cp_up() */
+ unsigned int if6_up :1; /* True when the IPv6 interface is up. */
+#endif /* PPP_IPV6_SUPPORT */
+ unsigned int lcp_echo_timer_running :1; /* set if a timer is running */
+#if VJ_SUPPORT
+ unsigned int vj_enabled :1; /* Flag indicating VJ compression enabled. */
+#endif /* VJ_SUPPORT */
+#if CCP_SUPPORT
+ unsigned int ccp_all_rejected :1; /* we rejected all peer's options */
+#endif /* CCP_SUPPORT */
+#if MPPE_SUPPORT
+ unsigned int mppe_keys_set :1; /* Have the MPPE keys been set? */
+#endif /* MPPE_SUPPORT */
+
+#if PPP_AUTH_SUPPORT
+ /* auth data */
+#if PPP_SERVER && defined(HAVE_MULTILINK)
+ char peer_authname[MAXNAMELEN + 1]; /* The name by which the peer authenticated itself to us. */
+#endif /* PPP_SERVER && defined(HAVE_MULTILINK) */
+ u16_t auth_pending; /* Records which authentication operations haven't completed yet. */
+ u16_t auth_done; /* Records which authentication operations have been completed. */
+
+#if PAP_SUPPORT
+ upap_state upap; /* PAP data */
+#endif /* PAP_SUPPORT */
+
+#if CHAP_SUPPORT
+ chap_client_state chap_client; /* CHAP client data */
+#if PPP_SERVER
+ chap_server_state chap_server; /* CHAP server data */
+#endif /* PPP_SERVER */
+#endif /* CHAP_SUPPORT */
+
+#if EAP_SUPPORT
+ eap_state eap; /* EAP data */
+#endif /* EAP_SUPPORT */
+#endif /* PPP_AUTH_SUPPORT */
+
+ fsm lcp_fsm; /* LCP fsm structure */
+ lcp_options lcp_wantoptions; /* Options that we want to request */
+ lcp_options lcp_gotoptions; /* Options that peer ack'd */
+ lcp_options lcp_allowoptions; /* Options we allow peer to request */
+ lcp_options lcp_hisoptions; /* Options that we ack'd */
+ u16_t peer_mru; /* currently negotiated peer MRU */
+ u8_t lcp_echos_pending; /* Number of outstanding echo msgs */
+ u8_t lcp_echo_number; /* ID number of next echo frame */
+
+ u8_t num_np_open; /* Number of network protocols which we have opened. */
+ u8_t num_np_up; /* Number of network protocols which have come up. */
+
+#if VJ_SUPPORT
+ struct vjcompress vj_comp; /* Van Jacobson compression header. */
+#endif /* VJ_SUPPORT */
+
+#if CCP_SUPPORT
+ fsm ccp_fsm; /* CCP fsm structure */
+ ccp_options ccp_wantoptions; /* what to request the peer to use */
+ ccp_options ccp_gotoptions; /* what the peer agreed to do */
+ ccp_options ccp_allowoptions; /* what we'll agree to do */
+ ccp_options ccp_hisoptions; /* what we agreed to do */
+ u8_t ccp_localstate; /* Local state (mainly for handling reset-reqs and reset-acks). */
+ u8_t ccp_receive_method; /* Method chosen on receive path */
+ u8_t ccp_transmit_method; /* Method chosen on transmit path */
+#if MPPE_SUPPORT
+ ppp_mppe_state mppe_comp; /* MPPE "compressor" structure */
+ ppp_mppe_state mppe_decomp; /* MPPE "decompressor" structure */
+#endif /* MPPE_SUPPORT */
+#endif /* CCP_SUPPORT */
+
+#if PPP_IPV4_SUPPORT
+ fsm ipcp_fsm; /* IPCP fsm structure */
+ ipcp_options ipcp_wantoptions; /* Options that we want to request */
+ ipcp_options ipcp_gotoptions; /* Options that peer ack'd */
+ ipcp_options ipcp_allowoptions; /* Options we allow peer to request */
+ ipcp_options ipcp_hisoptions; /* Options that we ack'd */
+#endif /* PPP_IPV4_SUPPORT */
+
+#if PPP_IPV6_SUPPORT
+ fsm ipv6cp_fsm; /* IPV6CP fsm structure */
+ ipv6cp_options ipv6cp_wantoptions; /* Options that we want to request */
+ ipv6cp_options ipv6cp_gotoptions; /* Options that peer ack'd */
+ ipv6cp_options ipv6cp_allowoptions; /* Options we allow peer to request */
+ ipv6cp_options ipv6cp_hisoptions; /* Options that we ack'd */
+#endif /* PPP_IPV6_SUPPORT */
+};
+
+/************************
+ *** PUBLIC FUNCTIONS ***
+ ************************/
+
+/*
+ * WARNING: For multi-threads environment, all ppp_set_* functions most
+ * only be called while the PPP is in the dead phase (i.e. disconnected).
+ */
+
+#if PPP_AUTH_SUPPORT
+/*
+ * Set PPP authentication.
+ *
+ * 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.
+ *
+ * Default is none auth type, unset (NULL) user and passwd.
+ */
+#define PPPAUTHTYPE_NONE 0x00
+#define PPPAUTHTYPE_PAP 0x01
+#define PPPAUTHTYPE_CHAP 0x02
+#define PPPAUTHTYPE_MSCHAP 0x04
+#define PPPAUTHTYPE_MSCHAP_V2 0x08
+#define PPPAUTHTYPE_EAP 0x10
+#define PPPAUTHTYPE_ANY 0xff
+void ppp_set_auth(ppp_pcb *pcb, u8_t authtype, const char *user, const char *passwd);
+
+/*
+ * If set, peer is required to authenticate. This is mostly necessary for PPP server support.
+ *
+ * Default is false.
+ */
+#define ppp_set_auth_required(ppp, boolval) (ppp->settings.auth_required = boolval)
+#endif /* PPP_AUTH_SUPPORT */
+
+#if PPP_IPV4_SUPPORT
+/*
+ * Set PPP interface "our" and "his" IPv4 addresses. This is mostly necessary for PPP server
+ * support but it can also be used on a PPP link where each side choose its own IP address.
+ *
+ * Default is unset (0.0.0.0).
+ */
+#define ppp_set_ipcp_ouraddr(ppp, addr) do { ppp->ipcp_wantoptions.ouraddr = ip4_addr_get_u32(addr); \
+ ppp->ask_for_local = ppp->ipcp_wantoptions.ouraddr != 0; } while(0)
+#define ppp_set_ipcp_hisaddr(ppp, addr) (ppp->ipcp_wantoptions.hisaddr = ip4_addr_get_u32(addr))
+#if LWIP_DNS
+/*
+ * Set DNS server addresses that are sent if the peer asks for them. This is mostly necessary
+ * for PPP server support.
+ *
+ * Default is unset (0.0.0.0).
+ */
+#define ppp_set_ipcp_dnsaddr(ppp, index, addr) (ppp->ipcp_allowoptions.dnsaddr[index] = ip4_addr_get_u32(addr))
+
+/*
+ * If set, we ask the peer for up to 2 DNS server addresses. Received DNS server addresses are
+ * registered using the dns_setserver() function.
+ *
+ * Default is false.
+ */
+#define ppp_set_usepeerdns(ppp, boolval) (ppp->settings.usepeerdns = boolval)
+#endif /* LWIP_DNS */
+#endif /* PPP_IPV4_SUPPORT */
+
+#if MPPE_SUPPORT
+/* Disable MPPE (Microsoft Point to Point Encryption). This parameter is exclusive. */
+#define PPP_MPPE_DISABLE 0x00
+/* Require the use of MPPE (Microsoft Point to Point Encryption). */
+#define PPP_MPPE_ENABLE 0x01
+/* Allow MPPE to use stateful mode. Stateless mode is still attempted first. */
+#define PPP_MPPE_ALLOW_STATEFUL 0x02
+/* Refuse the use of MPPE with 40-bit encryption. Conflict with PPP_MPPE_REFUSE_128. */
+#define PPP_MPPE_REFUSE_40 0x04
+/* Refuse the use of MPPE with 128-bit encryption. Conflict with PPP_MPPE_REFUSE_40. */
+#define PPP_MPPE_REFUSE_128 0x08
+/*
+ * Set MPPE configuration
+ *
+ * Default is disabled.
+ */
+void ppp_set_mppe(ppp_pcb *pcb, u8_t flags);
+#endif /* MPPE_SUPPORT */
+
+/*
+ * Wait for up to intval milliseconds for a valid PPP packet from the peer.
+ * At the end of this time, or when a valid PPP packet is received from the
+ * peer, we commence negotiation by sending our first LCP packet.
+ *
+ * Default is 0.
+ */
+#define ppp_set_listen_time(ppp, intval) (ppp->settings.listen_time = intval)
+
+/*
+ * If set, we will attempt to initiate a connection but if no reply is received from
+ * the peer, we will then just wait passively for a valid LCP packet from the peer.
+ *
+ * Default is false.
+ */
+#define ppp_set_passive(ppp, boolval) (ppp->lcp_wantoptions.passive = boolval)
+
+/*
+ * If set, we will not transmit LCP packets to initiate a connection until a valid
+ * LCP packet is received from the peer. This is what we usually call the server mode.
+ *
+ * Default is false.
+ */
+#define ppp_set_silent(ppp, boolval) (ppp->lcp_wantoptions.silent = boolval)
+
+/*
+ * If set, enable protocol field compression negotiation in both the receive and
+ * the transmit direction.
+ *
+ * Default is true.
+ */
+#define ppp_set_neg_pcomp(ppp, boolval) (ppp->lcp_wantoptions.neg_pcompression = \
+ ppp->lcp_allowoptions.neg_pcompression = boolval)
+
+/*
+ * If set, enable Address/Control compression in both the receive and the transmit
+ * direction.
+ *
+ * Default is true.
+ */
+#define ppp_set_neg_accomp(ppp, boolval) (ppp->lcp_wantoptions.neg_accompression = \
+ ppp->lcp_allowoptions.neg_accompression = boolval)
+
+/*
+ * If set, enable asyncmap negotiation. Otherwise forcing all control characters to
+ * be escaped for both the transmit and the receive direction.
+ *
+ * Default is true.
+ */
+#define ppp_set_neg_asyncmap(ppp, boolval) (ppp->lcp_wantoptions.neg_asyncmap = \
+ ppp->lcp_allowoptions.neg_asyncmap = boolval)
+
+/*
+ * This option sets the Async-Control-Character-Map (ACCM) for this end of the link.
+ * The ACCM is a set of 32 bits, one for each of the ASCII control characters with
+ * values from 0 to 31, where a 1 bit indicates that the corresponding control
+ * character should not be used in PPP packets sent to this system. The map is
+ * an unsigned 32 bits integer where the least significant bit (00000001) represents
+ * character 0 and the most significant bit (80000000) represents character 31.
+ * We will then ask the peer to send these characters as a 2-byte escape sequence.
+ *
+ * Default is 0.
+ */
+#define ppp_set_asyncmap(ppp, intval) (ppp->lcp_wantoptions.asyncmap = intval)
+
+/*
+ * Set a PPP interface as the default network interface
+ * (used to output all packets for which no specific route is found).
+ */
+#define ppp_set_default(ppp) netif_set_default(ppp->netif)
+
+#if PPP_NOTIFY_PHASE
+/*
+ * Set a PPP notify phase callback.
+ *
+ * This can be used for example to set a LED pattern depending on the
+ * current phase of the PPP session.
+ */
+typedef void (*ppp_notify_phase_cb_fn)(ppp_pcb *pcb, u8_t phase, void *ctx);
+void ppp_set_notify_phase_callback(ppp_pcb *pcb, ppp_notify_phase_cb_fn notify_phase_cb);
+#endif /* PPP_NOTIFY_PHASE */
+
+/*
+ * Initiate a PPP connection.
+ *
+ * This can only be called if PPP is in the dead phase.
+ *
+ * Holdoff is the time to wait (in seconds) before initiating
+ * the connection.
+ *
+ * If this port connects to a modem, the modem connection must be
+ * established before calling this.
+ */
+err_t ppp_connect(ppp_pcb *pcb, u16_t holdoff);
+
+#if PPP_SERVER
+/*
+ * Listen for an incoming PPP connection.
+ *
+ * This can only be called if PPP is in the dead phase.
+ *
+ * If this port connects to a modem, the modem connection must be
+ * established before calling this.
+ */
+err_t ppp_listen(ppp_pcb *pcb);
+#endif /* PPP_SERVER */
+
+/*
+ * Initiate the end of a PPP connection.
+ * Any outstanding packets in the queues are dropped.
+ *
+ * Setting nocarrier to 1 close the PPP connection without initiating the
+ * shutdown procedure. Always using nocarrier = 0 is still recommended,
+ * this is going to take a little longer time if your link is down, but
+ * is a safer choice for the PPP state machine.
+ *
+ * Return 0 on success, an error code on failure.
+ */
+err_t ppp_close(ppp_pcb *pcb, u8_t nocarrier);
+
+/*
+ * Release the control block.
+ *
+ * This can only be called if PPP is in the dead phase.
+ *
+ * You must use ppp_close() before if you wish to terminate
+ * an established PPP session.
+ *
+ * Return 0 on success, an error code on failure.
+ */
+err_t ppp_free(ppp_pcb *pcb);
+
+/*
+ * PPP IOCTL commands.
+ *
+ * Get the up status - 0 for down, non-zero for up. The argument must
+ * point to an int.
+ */
+#define PPPCTLG_UPSTATUS 0
+
+/*
+ * Get the PPP error code. The argument must point to an int.
+ * Returns a PPPERR_* value.
+ */
+#define PPPCTLG_ERRCODE 1
+
+/*
+ * Get the fd associated with a PPP over serial
+ */
+#define PPPCTLG_FD 2
+
+/*
+ * Get and set parameters for the given connection.
+ * Return 0 on success, an error code on failure.
+ */
+err_t ppp_ioctl(ppp_pcb *pcb, u8_t cmd, void *arg);
+
+/* Get the PPP netif interface */
+#define ppp_netif(ppp) (ppp->netif)
+
+/* Set an lwIP-style status-callback for the selected PPP device */
+#define ppp_set_netif_statuscallback(ppp, status_cb) \
+ netif_set_status_callback(ppp->netif, status_cb);
+
+/* Set an lwIP-style link-callback for the selected PPP device */
+#define ppp_set_netif_linkcallback(ppp, link_cb) \
+ netif_set_link_callback(ppp->netif, link_cb);
+
+#endif /* PPP_H */
+
+#endif /* PPP_SUPPORT */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/ppp_impl.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/ppp_impl.h
new file mode 100644
index 0000000..1d4c774
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/ppp_impl.h
@@ -0,0 +1,629 @@
+/*****************************************************************************
+* 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 LWIP_HDR_PPP_IMPL_H
+#define LWIP_HDR_PPP_IMPL_H
+
+#include "netif/ppp/ppp_opts.h"
+
+#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#ifdef PPP_INCLUDE_SETTINGS_HEADER
+#include "ppp_settings.h"
+#endif
+
+#include <stdio.h> /* formats */
+#include <stdarg.h>
+#include <string.h>
+#include <stdlib.h> /* strtol() */
+
+#include "lwip/netif.h"
+#include "lwip/def.h"
+#include "lwip/timeouts.h"
+
+#include "ppp.h"
+#include "pppdebug.h"
+
+/*
+ * Memory used for control packets.
+ *
+ * PPP_CTRL_PBUF_MAX_SIZE is the amount of memory we allocate when we
+ * cannot figure out how much we are going to use before filling the buffer.
+ */
+#if PPP_USE_PBUF_RAM
+#define PPP_CTRL_PBUF_TYPE PBUF_RAM
+#define PPP_CTRL_PBUF_MAX_SIZE 512
+#else /* PPP_USE_PBUF_RAM */
+#define PPP_CTRL_PBUF_TYPE PBUF_POOL
+#define PPP_CTRL_PBUF_MAX_SIZE PBUF_POOL_BUFSIZE
+#endif /* PPP_USE_PBUF_RAM */
+
+/*
+ * 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])
+
+/*
+ * 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 */
+#if 0 /* UNUSED */
+#define PPP_AT 0x29 /* AppleTalk Protocol */
+#define PPP_IPX 0x2b /* IPX protocol */
+#endif /* UNUSED */
+#if VJ_SUPPORT
+#define PPP_VJC_COMP 0x2d /* VJ compressed TCP */
+#define PPP_VJC_UNCOMP 0x2f /* VJ uncompressed TCP */
+#endif /* VJ_SUPPORT */
+#if PPP_IPV6_SUPPORT
+#define PPP_IPV6 0x57 /* Internet Protocol Version 6 */
+#endif /* PPP_IPV6_SUPPORT */
+#if CCP_SUPPORT
+#define PPP_COMP 0xfd /* compressed packet */
+#endif /* CCP_SUPPORT */
+#define PPP_IPCP 0x8021 /* IP Control Protocol */
+#if 0 /* UNUSED */
+#define PPP_ATCP 0x8029 /* AppleTalk Control Protocol */
+#define PPP_IPXCP 0x802b /* IPX Control Protocol */
+#endif /* UNUSED */
+#if PPP_IPV6_SUPPORT
+#define PPP_IPV6CP 0x8057 /* IPv6 Control Protocol */
+#endif /* PPP_IPV6_SUPPORT */
+#if CCP_SUPPORT
+#define PPP_CCP 0x80fd /* Compression Control Protocol */
+#endif /* CCP_SUPPORT */
+#if ECP_SUPPORT
+#define PPP_ECP 0x8053 /* Encryption Control Protocol */
+#endif /* ECP_SUPPORT */
+#define PPP_LCP 0xc021 /* Link Control Protocol */
+#if PAP_SUPPORT
+#define PPP_PAP 0xc023 /* Password Authentication Protocol */
+#endif /* PAP_SUPPORT */
+#if LQR_SUPPORT
+#define PPP_LQR 0xc025 /* Link Quality Report protocol */
+#endif /* LQR_SUPPORT */
+#if CHAP_SUPPORT
+#define PPP_CHAP 0xc223 /* Cryptographic Handshake Auth. Protocol */
+#endif /* CHAP_SUPPORT */
+#if CBCP_SUPPORT
+#define PPP_CBCP 0xc029 /* Callback Control Protocol */
+#endif /* CBCP_SUPPORT */
+#if EAP_SUPPORT
+#define PPP_EAP 0xc227 /* Extensible Authentication Protocol */
+#endif /* EAP_SUPPORT */
+
+/*
+ * The following struct gives the addresses of procedures to call
+ * for a particular lower link level protocol.
+ */
+struct link_callbacks {
+ /* Start a connection (e.g. Initiate discovery phase) */
+ void (*connect) (ppp_pcb *pcb, void *ctx);
+#if PPP_SERVER
+ /* Listen for an incoming connection (Passive mode) */
+ void (*listen) (ppp_pcb *pcb, void *ctx);
+#endif /* PPP_SERVER */
+ /* End a connection (i.e. initiate disconnect phase) */
+ void (*disconnect) (ppp_pcb *pcb, void *ctx);
+ /* Free lower protocol control block */
+ err_t (*free) (ppp_pcb *pcb, void *ctx);
+ /* Write a pbuf to a ppp link, only used from PPP functions to send PPP packets. */
+ err_t (*write)(ppp_pcb *pcb, void *ctx, struct pbuf *p);
+ /* Send a packet from lwIP core (IPv4 or IPv6) */
+ err_t (*netif_output)(ppp_pcb *pcb, void *ctx, struct pbuf *p, u_short protocol);
+ /* configure the transmit-side characteristics of the PPP interface */
+ void (*send_config)(ppp_pcb *pcb, void *ctx, u32_t accm, int pcomp, int accomp);
+ /* confire the receive-side characteristics of the PPP interface */
+ void (*recv_config)(ppp_pcb *pcb, void *ctx, u32_t accm, int pcomp, int accomp);
+};
+
+/*
+ * 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. */
+};
+
+/*
+ * Statistics.
+ */
+#if PPP_STATS_SUPPORT
+struct pppstat {
+ unsigned int ppp_ibytes; /* bytes received */
+ unsigned int ppp_ipackets; /* packets received */
+ unsigned int ppp_ierrors; /* receive errors */
+ unsigned int ppp_obytes; /* bytes sent */
+ unsigned int ppp_opackets; /* packets sent */
+ unsigned int ppp_oerrors; /* transmit errors */
+};
+
+#if VJ_SUPPORT
+struct vjstat {
+ unsigned int vjs_packets; /* outbound packets */
+ unsigned int vjs_compressed; /* outbound compressed packets */
+ unsigned int vjs_searches; /* searches for connection state */
+ unsigned int vjs_misses; /* times couldn't find conn. state */
+ unsigned int vjs_uncompressedin; /* inbound uncompressed packets */
+ unsigned int vjs_compressedin; /* inbound compressed packets */
+ unsigned int vjs_errorin; /* inbound unknown type packets */
+ unsigned int vjs_tossed; /* inbound packets tossed because of error */
+};
+#endif /* VJ_SUPPORT */
+
+struct ppp_stats {
+ struct pppstat p; /* basic PPP statistics */
+#if VJ_SUPPORT
+ struct vjstat vj; /* VJ header compression statistics */
+#endif /* VJ_SUPPORT */
+};
+
+#if CCP_SUPPORT
+struct compstat {
+ unsigned int unc_bytes; /* total uncompressed bytes */
+ unsigned int unc_packets; /* total uncompressed packets */
+ unsigned int comp_bytes; /* compressed bytes */
+ unsigned int comp_packets; /* compressed packets */
+ unsigned int inc_bytes; /* incompressible bytes */
+ unsigned int inc_packets; /* incompressible packets */
+ unsigned int ratio; /* recent compression ratio << 8 */
+};
+
+struct ppp_comp_stats {
+ struct compstat c; /* packet compression statistics */
+ struct compstat d; /* packet decompression statistics */
+};
+#endif /* CCP_SUPPORT */
+
+#endif /* PPP_STATS_SUPPORT */
+
+#if PPP_IDLETIMELIMIT
+/*
+ * The following structure records the time in seconds since
+ * the last NP packet was sent or received.
+ */
+struct ppp_idle {
+ time_t xmit_idle; /* time since last NP packet sent */
+ time_t recv_idle; /* time since last NP packet received */
+};
+#endif /* PPP_IDLETIMELIMIT */
+
+/* values for epdisc.class */
+#define EPD_NULL 0 /* null discriminator, no data */
+#define EPD_LOCAL 1
+#define EPD_IP 2
+#define EPD_MAC 3
+#define EPD_MAGIC 4
+#define EPD_PHONENUM 5
+
+/*
+ * Global variables.
+ */
+#ifdef HAVE_MULTILINK
+extern u8_t multilink; /* enable multilink operation */
+extern u8_t doing_multilink;
+extern u8_t multilink_master;
+extern u8_t bundle_eof;
+extern u8_t bundle_terminating;
+#endif
+
+#ifdef MAXOCTETS
+extern unsigned int maxoctets; /* Maximum octetes per session (in bytes) */
+extern int maxoctets_dir; /* Direction :
+ 0 - in+out (default)
+ 1 - in
+ 2 - out
+ 3 - max(in,out) */
+extern int maxoctets_timeout; /* Timeout for check of octets limit */
+#define PPP_OCTETS_DIRECTION_SUM 0
+#define PPP_OCTETS_DIRECTION_IN 1
+#define PPP_OCTETS_DIRECTION_OUT 2
+#define PPP_OCTETS_DIRECTION_MAXOVERAL 3
+/* same as previos, but little different on RADIUS side */
+#define PPP_OCTETS_DIRECTION_MAXSESSION 4
+#endif
+
+/* Data input may be used by CCP and ECP, remove this entry
+ * from struct protent to save some flash
+ */
+#define PPP_DATAINPUT 0
+
+/*
+ * 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) (ppp_pcb *pcb);
+ /* Process a received packet */
+ void (*input) (ppp_pcb *pcb, u_char *pkt, int len);
+ /* Process a received protocol-reject */
+ void (*protrej) (ppp_pcb *pcb);
+ /* Lower layer has come up */
+ void (*lowerup) (ppp_pcb *pcb);
+ /* Lower layer has gone down */
+ void (*lowerdown) (ppp_pcb *pcb);
+ /* Open the protocol */
+ void (*open) (ppp_pcb *pcb);
+ /* Close the protocol */
+ void (*close) (ppp_pcb *pcb, const char *reason);
+#if PRINTPKT_SUPPORT
+ /* Print a packet in readable form */
+ int (*printpkt) (const u_char *pkt, int len,
+ void (*printer) (void *, const char *, ...),
+ void *arg);
+#endif /* PRINTPKT_SUPPORT */
+#if PPP_DATAINPUT
+ /* Process a received data packet */
+ void (*datainput) (ppp_pcb *pcb, u_char *pkt, int len);
+#endif /* PPP_DATAINPUT */
+#if PRINTPKT_SUPPORT
+ const char *name; /* Text name of protocol */
+ const char *data_name; /* Text name of corresponding data protocol */
+#endif /* PRINTPKT_SUPPORT */
+#if PPP_OPTIONS
+ option_t *options; /* List of command-line options */
+ /* Check requested options, assign defaults */
+ void (*check_options) (void);
+#endif /* PPP_OPTIONS */
+#if DEMAND_SUPPORT
+ /* 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 /* DEMAND_SUPPORT */
+};
+
+/* Table of pointers to supported protocols */
+extern const struct protent* const protocols[];
+
+
+/* Values for auth_pending, auth_done */
+#if PAP_SUPPORT
+#define PAP_WITHPEER 0x1
+#define PAP_PEER 0x2
+#endif /* PAP_SUPPORT */
+#if CHAP_SUPPORT
+#define CHAP_WITHPEER 0x4
+#define CHAP_PEER 0x8
+#endif /* CHAP_SUPPORT */
+#if EAP_SUPPORT
+#define EAP_WITHPEER 0x10
+#define EAP_PEER 0x20
+#endif /* EAP_SUPPORT */
+
+/* Values for auth_done only */
+#if CHAP_SUPPORT
+#define CHAP_MD5_WITHPEER 0x40
+#define CHAP_MD5_PEER 0x80
+#if MSCHAP_SUPPORT
+#define CHAP_MS_SHIFT 8 /* LSB position for MS auths */
+#define CHAP_MS_WITHPEER 0x100
+#define CHAP_MS_PEER 0x200
+#define CHAP_MS2_WITHPEER 0x400
+#define CHAP_MS2_PEER 0x800
+#endif /* MSCHAP_SUPPORT */
+#endif /* CHAP_SUPPORT */
+
+/* Supported CHAP protocols */
+#if CHAP_SUPPORT
+
+#if MSCHAP_SUPPORT
+#define CHAP_MDTYPE_SUPPORTED (MDTYPE_MICROSOFT_V2 | MDTYPE_MICROSOFT | MDTYPE_MD5)
+#else /* MSCHAP_SUPPORT */
+#define CHAP_MDTYPE_SUPPORTED (MDTYPE_MD5)
+#endif /* MSCHAP_SUPPORT */
+
+#else /* CHAP_SUPPORT */
+#define CHAP_MDTYPE_SUPPORTED (MDTYPE_NONE)
+#endif /* CHAP_SUPPORT */
+
+#if PPP_STATS_SUPPORT
+/*
+ * PPP statistics structure
+ */
+struct pppd_stats {
+ unsigned int bytes_in;
+ unsigned int bytes_out;
+ unsigned int pkts_in;
+ unsigned int pkts_out;
+};
+#endif /* PPP_STATS_SUPPORT */
+
+
+/*
+ * PPP private functions
+ */
+
+
+/*
+ * Functions called from lwIP core.
+ */
+
+/* initialize the PPP subsystem */
+int ppp_init(void);
+
+/*
+ * Functions called from PPP link protocols.
+ */
+
+/* Create a new PPP control block */
+ppp_pcb *ppp_new(struct netif *pppif, const struct link_callbacks *callbacks, void *link_ctx_cb,
+ ppp_link_status_cb_fn link_status_cb, void *ctx_cb);
+
+/* Initiate LCP open request */
+void ppp_start(ppp_pcb *pcb);
+
+/* Called when link failed to setup */
+void ppp_link_failed(ppp_pcb *pcb);
+
+/* Called when link is normally down (i.e. it was asked to end) */
+void ppp_link_end(ppp_pcb *pcb);
+
+/* function called to process input packet */
+void ppp_input(ppp_pcb *pcb, struct pbuf *pb);
+
+/* helper function, merge a pbuf chain into one pbuf */
+struct pbuf *ppp_singlebuf(struct pbuf *p);
+
+
+/*
+ * Functions called by PPP protocols.
+ */
+
+/* function called by all PPP subsystems to send packets */
+err_t ppp_write(ppp_pcb *pcb, struct pbuf *p);
+
+/* functions called by auth.c link_terminated() */
+void ppp_link_terminated(ppp_pcb *pcb);
+
+void new_phase(ppp_pcb *pcb, int p);
+
+int ppp_send_config(ppp_pcb *pcb, int mtu, u32_t accm, int pcomp, int accomp);
+int ppp_recv_config(ppp_pcb *pcb, int mru, u32_t accm, int pcomp, int accomp);
+
+#if PPP_IPV4_SUPPORT
+int sifaddr(ppp_pcb *pcb, u32_t our_adr, u32_t his_adr, u32_t netmask);
+int cifaddr(ppp_pcb *pcb, u32_t our_adr, u32_t his_adr);
+#if 0 /* UNUSED - PROXY ARP */
+int sifproxyarp(ppp_pcb *pcb, u32_t his_adr);
+int cifproxyarp(ppp_pcb *pcb, u32_t his_adr);
+#endif /* UNUSED - PROXY ARP */
+#if LWIP_DNS
+int sdns(ppp_pcb *pcb, u32_t ns1, u32_t ns2);
+int cdns(ppp_pcb *pcb, u32_t ns1, u32_t ns2);
+#endif /* LWIP_DNS */
+#if VJ_SUPPORT
+int sifvjcomp(ppp_pcb *pcb, int vjcomp, int cidcomp, int maxcid);
+#endif /* VJ_SUPPORT */
+int sifup(ppp_pcb *pcb);
+int sifdown (ppp_pcb *pcb);
+u32_t get_mask(u32_t addr);
+#endif /* PPP_IPV4_SUPPORT */
+
+#if PPP_IPV6_SUPPORT
+int sif6addr(ppp_pcb *pcb, eui64_t our_eui64, eui64_t his_eui64);
+int cif6addr(ppp_pcb *pcb, eui64_t our_eui64, eui64_t his_eui64);
+int sif6up(ppp_pcb *pcb);
+int sif6down (ppp_pcb *pcb);
+#endif /* PPP_IPV6_SUPPORT */
+
+#if DEMAND_SUPPORT
+int sifnpmode(ppp_pcb *pcb, int proto, enum NPmode mode);
+#endif /* DEMAND_SUPPORt */
+
+void netif_set_mtu(ppp_pcb *pcb, int mtu);
+int netif_get_mtu(ppp_pcb *pcb);
+
+#if CCP_SUPPORT
+#if 0 /* unused */
+int ccp_test(ppp_pcb *pcb, u_char *opt_ptr, int opt_len, int for_transmit);
+#endif /* unused */
+void ccp_set(ppp_pcb *pcb, u8_t isopen, u8_t isup, u8_t receive_method, u8_t transmit_method);
+void ccp_reset_comp(ppp_pcb *pcb);
+void ccp_reset_decomp(ppp_pcb *pcb);
+#if 0 /* unused */
+int ccp_fatal_error(ppp_pcb *pcb);
+#endif /* unused */
+#endif /* CCP_SUPPORT */
+
+#if PPP_IDLETIMELIMIT
+int get_idle_time(ppp_pcb *pcb, struct ppp_idle *ip);
+#endif /* PPP_IDLETIMELIMIT */
+
+#if DEMAND_SUPPORT
+int get_loop_output(void);
+#endif /* DEMAND_SUPPORT */
+
+/* Optional protocol names list, to make our messages a little more informative. */
+#if PPP_PROTOCOLNAME
+const char * protocol_name(int proto);
+#endif /* PPP_PROTOCOLNAME */
+
+/* Optional stats support, to get some statistics on the PPP interface */
+#if PPP_STATS_SUPPORT
+void print_link_stats(void); /* Print stats, if available */
+void reset_link_stats(int u); /* Reset (init) stats when link goes up */
+void update_link_stats(int u); /* Get stats at link termination */
+#endif /* PPP_STATS_SUPPORT */
+
+
+
+/*
+ * 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)++ << 8; \
+ (s) |= *(cp)++; \
+}
+#define PUTSHORT(s, cp) { \
+ *(cp)++ = (u_char) ((s) >> 8); \
+ *(cp)++ = (u_char) (s); \
+}
+#define GETLONG(l, cp) { \
+ (l) = *(cp)++ << 8; \
+ (l) |= *(cp)++; (l) <<= 8; \
+ (l) |= *(cp)++; (l) <<= 8; \
+ (l) |= *(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))
+
+/*
+ * System dependent definitions for user-level 4.3BSD UNIX implementation.
+ */
+#define TIMEOUT(f, a, t) do { sys_untimeout((f), (a)); sys_timeout((t)*1000, (f), (a)); } while(0)
+#define TIMEOUTMS(f, a, t) do { sys_untimeout((f), (a)); sys_timeout((t), (f), (a)); } while(0)
+#define UNTIMEOUT(f, a) sys_untimeout((f), (a))
+
+#define BZERO(s, n) memset(s, 0, n)
+#define BCMP(s1, s2, l) memcmp(s1, s2, l)
+
+#define PRINTMSG(m, l) { ppp_info("Remote message: %0.*v", l, m); }
+
+/*
+ * MAKEHEADER - Add Header fields to a packet.
+ */
+#define MAKEHEADER(p, t) { \
+ PUTCHAR(PPP_ALLSTATIONS, p); \
+ PUTCHAR(PPP_UI, p); \
+ PUTSHORT(t, p); }
+
+/* Procedures exported from auth.c */
+void link_required(ppp_pcb *pcb); /* we are starting to use the link */
+void link_terminated(ppp_pcb *pcb); /* we are finished with the link */
+void link_down(ppp_pcb *pcb); /* the LCP layer has left the Opened state */
+void upper_layers_down(ppp_pcb *pcb); /* take all NCPs down */
+void link_established(ppp_pcb *pcb); /* the link is up; authenticate now */
+void start_networks(ppp_pcb *pcb); /* start all the network control protos */
+void continue_networks(ppp_pcb *pcb); /* start network [ip, etc] control protos */
+#if PPP_AUTH_SUPPORT
+#if PPP_SERVER
+int auth_check_passwd(ppp_pcb *pcb, char *auser, int userlen, char *apasswd, int passwdlen, const char **msg, int *msglen);
+ /* check the user name and passwd against configuration */
+void auth_peer_fail(ppp_pcb *pcb, int protocol);
+ /* peer failed to authenticate itself */
+void auth_peer_success(ppp_pcb *pcb, int protocol, int prot_flavor, const char *name, int namelen);
+ /* peer successfully authenticated itself */
+#endif /* PPP_SERVER */
+void auth_withpeer_fail(ppp_pcb *pcb, int protocol);
+ /* we failed to authenticate ourselves */
+void auth_withpeer_success(ppp_pcb *pcb, int protocol, int prot_flavor);
+ /* we successfully authenticated ourselves */
+#endif /* PPP_AUTH_SUPPORT */
+void np_up(ppp_pcb *pcb, int proto); /* a network protocol has come up */
+void np_down(ppp_pcb *pcb, int proto); /* a network protocol has gone down */
+void np_finished(ppp_pcb *pcb, int proto); /* a network protocol no longer needs link */
+#if PPP_AUTH_SUPPORT
+int get_secret(ppp_pcb *pcb, const char *client, const char *server, char *secret, int *secret_len, int am_server);
+ /* get "secret" for chap */
+#endif /* PPP_AUTH_SUPPORT */
+
+/* Procedures exported from ipcp.c */
+/* int parse_dotted_ip (char *, u32_t *); */
+
+/* Procedures exported from demand.c */
+#if DEMAND_SUPPORT
+void demand_conf (void); /* config interface(s) for demand-dial */
+void demand_block (void); /* set all NPs to queue up packets */
+void demand_unblock (void); /* set all NPs to pass packets */
+void demand_discard (void); /* set all NPs to discard packets */
+void demand_rexmit (int, u32_t); /* retransmit saved frames for an NP*/
+int loop_chars (unsigned char *, int); /* process chars from loopback */
+int loop_frame (unsigned char *, int); /* should we bring link up? */
+#endif /* DEMAND_SUPPORT */
+
+/* Procedures exported from multilink.c */
+#ifdef HAVE_MULTILINK
+void mp_check_options (void); /* Check multilink-related options */
+int mp_join_bundle (void); /* join our link to an appropriate bundle */
+void mp_exit_bundle (void); /* have disconnected our link from bundle */
+void mp_bundle_terminated (void);
+char *epdisc_to_str (struct epdisc *); /* string from endpoint discrim. */
+int str_to_epdisc (struct epdisc *, char *); /* endpt disc. from str */
+#else
+#define mp_bundle_terminated() /* nothing */
+#define mp_exit_bundle() /* nothing */
+#define doing_multilink 0
+#define multilink_master 0
+#endif
+
+/* Procedures exported from utils.c. */
+void ppp_print_string(const u_char *p, int len, void (*printer) (void *, const char *, ...), void *arg); /* Format a string for output */
+int ppp_slprintf(char *buf, int buflen, const char *fmt, ...); /* sprintf++ */
+int ppp_vslprintf(char *buf, int buflen, const char *fmt, va_list args); /* vsprintf++ */
+size_t ppp_strlcpy(char *dest, const char *src, size_t len); /* safe strcpy */
+size_t ppp_strlcat(char *dest, const char *src, size_t len); /* safe strncpy */
+void ppp_dbglog(const char *fmt, ...); /* log a debug message */
+void ppp_info(const char *fmt, ...); /* log an informational message */
+void ppp_notice(const char *fmt, ...); /* log a notice-level message */
+void ppp_warn(const char *fmt, ...); /* log a warning message */
+void ppp_error(const char *fmt, ...); /* log an error message */
+void ppp_fatal(const char *fmt, ...); /* log an error message and die(1) */
+#if PRINTPKT_SUPPORT
+void ppp_dump_packet(ppp_pcb *pcb, const char *tag, unsigned char *p, int len);
+ /* dump packet to debug log if interesting */
+#endif /* PRINTPKT_SUPPORT */
+
+
+#endif /* PPP_SUPPORT */
+#endif /* LWIP_HDR_PPP_IMPL_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/ppp_opts.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/ppp_opts.h
new file mode 100644
index 0000000..fa79c09
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/ppp_opts.h
@@ -0,0 +1,593 @@
+/*
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ */
+
+#ifndef LWIP_PPP_OPTS_H
+#define LWIP_PPP_OPTS_H
+
+#include "lwip/opt.h"
+
+/**
+ * PPP_SUPPORT==1: Enable PPP.
+ */
+#ifndef PPP_SUPPORT
+#define PPP_SUPPORT 0
+#endif
+
+/**
+ * PPPOE_SUPPORT==1: Enable PPP Over Ethernet
+ */
+#ifndef PPPOE_SUPPORT
+#define PPPOE_SUPPORT 0
+#endif
+
+/**
+ * PPPOL2TP_SUPPORT==1: Enable PPP Over L2TP
+ */
+#ifndef PPPOL2TP_SUPPORT
+#define PPPOL2TP_SUPPORT 0
+#endif
+
+/**
+ * PPPOL2TP_AUTH_SUPPORT==1: Enable PPP Over L2TP Auth (enable MD5 support)
+ */
+#ifndef PPPOL2TP_AUTH_SUPPORT
+#define PPPOL2TP_AUTH_SUPPORT PPPOL2TP_SUPPORT
+#endif
+
+/**
+ * PPPOS_SUPPORT==1: Enable PPP Over Serial
+ */
+#ifndef PPPOS_SUPPORT
+#define PPPOS_SUPPORT PPP_SUPPORT
+#endif
+
+/**
+ * LWIP_PPP_API==1: Enable PPP API (in pppapi.c)
+ */
+#ifndef LWIP_PPP_API
+#define LWIP_PPP_API (PPP_SUPPORT && (NO_SYS == 0))
+#endif
+
+/**
+ * MEMP_NUM_PPP_PCB: the number of simultaneously active PPP
+ * connections (requires the PPP_SUPPORT option)
+ */
+#ifndef MEMP_NUM_PPP_PCB
+#define MEMP_NUM_PPP_PCB 1
+#endif
+
+#if PPP_SUPPORT
+
+/**
+ * MEMP_NUM_PPPOS_INTERFACES: the number of concurrently active PPPoS
+ * interfaces (only used with PPPOS_SUPPORT==1)
+ */
+#ifndef MEMP_NUM_PPPOS_INTERFACES
+#define MEMP_NUM_PPPOS_INTERFACES MEMP_NUM_PPP_PCB
+#endif
+
+/**
+ * MEMP_NUM_PPPOE_INTERFACES: the number of concurrently active PPPoE
+ * interfaces (only used with PPPOE_SUPPORT==1)
+ */
+#ifndef MEMP_NUM_PPPOE_INTERFACES
+#define MEMP_NUM_PPPOE_INTERFACES 1
+#endif
+
+/**
+ * MEMP_NUM_PPPOL2TP_INTERFACES: the number of concurrently active PPPoL2TP
+ * interfaces (only used with PPPOL2TP_SUPPORT==1)
+ */
+#ifndef MEMP_NUM_PPPOL2TP_INTERFACES
+#define MEMP_NUM_PPPOL2TP_INTERFACES 1
+#endif
+
+/**
+ * MEMP_NUM_PPP_API_MSG: Number of concurrent PPP API messages (in pppapi.c)
+ */
+#ifndef MEMP_NUM_PPP_API_MSG
+#define MEMP_NUM_PPP_API_MSG 5
+#endif
+
+/**
+ * PPP_DEBUG: Enable debugging for PPP.
+ */
+#ifndef PPP_DEBUG
+#define PPP_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * PPP_INPROC_IRQ_SAFE==1 call pppos_input() using tcpip_callback().
+ *
+ * Please read the "PPPoS input path" chapter in the PPP documentation about this option.
+ */
+#ifndef PPP_INPROC_IRQ_SAFE
+#define PPP_INPROC_IRQ_SAFE 0
+#endif
+
+/**
+ * PRINTPKT_SUPPORT==1: Enable PPP print packet support
+ *
+ * Mandatory for debugging, it displays exchanged packet content in debug trace.
+ */
+#ifndef PRINTPKT_SUPPORT
+#define PRINTPKT_SUPPORT 0
+#endif
+
+/**
+ * PPP_IPV4_SUPPORT==1: Enable PPP IPv4 support
+ */
+#ifndef PPP_IPV4_SUPPORT
+#define PPP_IPV4_SUPPORT (LWIP_IPV4)
+#endif
+
+/**
+ * PPP_IPV6_SUPPORT==1: Enable PPP IPv6 support
+ */
+#ifndef PPP_IPV6_SUPPORT
+#define PPP_IPV6_SUPPORT (LWIP_IPV6)
+#endif
+
+/**
+ * PPP_NOTIFY_PHASE==1: Support PPP notify phase support
+ *
+ * PPP notify phase support allows you to set a callback which is
+ * called on change of the internal PPP state machine.
+ *
+ * This can be used for example to set a LED pattern depending on the
+ * current phase of the PPP session.
+ */
+#ifndef PPP_NOTIFY_PHASE
+#define PPP_NOTIFY_PHASE 0
+#endif
+
+/**
+ * pbuf_type PPP is using for LCP, PAP, CHAP, EAP, CCP, IPCP and IP6CP packets.
+ *
+ * Memory allocated must be single buffered for PPP to works, it requires pbuf
+ * that are not going to be chained when allocated. This requires setting
+ * PBUF_POOL_BUFSIZE to at least 512 bytes, which is quite huge for small systems.
+ *
+ * Setting PPP_USE_PBUF_RAM to 1 makes PPP use memory from heap where continuous
+ * buffers are required, allowing you to use a smaller PBUF_POOL_BUFSIZE.
+ */
+#ifndef PPP_USE_PBUF_RAM
+#define PPP_USE_PBUF_RAM 0
+#endif
+
+/**
+ * PPP_FCS_TABLE: Keep a 256*2 byte table to speed up FCS calculation for PPPoS
+ */
+#ifndef PPP_FCS_TABLE
+#define PPP_FCS_TABLE 1
+#endif
+
+/**
+ * PAP_SUPPORT==1: Support PAP.
+ */
+#ifndef PAP_SUPPORT
+#define PAP_SUPPORT 0
+#endif
+
+/**
+ * CHAP_SUPPORT==1: Support CHAP.
+ */
+#ifndef CHAP_SUPPORT
+#define CHAP_SUPPORT 0
+#endif
+
+/**
+ * MSCHAP_SUPPORT==1: Support MSCHAP.
+ */
+#ifndef MSCHAP_SUPPORT
+#define MSCHAP_SUPPORT 0
+#endif
+#if MSCHAP_SUPPORT
+/* MSCHAP requires CHAP support */
+#undef CHAP_SUPPORT
+#define CHAP_SUPPORT 1
+#endif /* MSCHAP_SUPPORT */
+
+/**
+ * EAP_SUPPORT==1: Support EAP.
+ */
+#ifndef EAP_SUPPORT
+#define EAP_SUPPORT 0
+#endif
+
+/**
+ * CCP_SUPPORT==1: Support CCP.
+ */
+#ifndef CCP_SUPPORT
+#define CCP_SUPPORT 0
+#endif
+
+/**
+ * MPPE_SUPPORT==1: Support MPPE.
+ */
+#ifndef MPPE_SUPPORT
+#define MPPE_SUPPORT 0
+#endif
+#if MPPE_SUPPORT
+/* MPPE requires CCP support */
+#undef CCP_SUPPORT
+#define CCP_SUPPORT 1
+/* MPPE requires MSCHAP support */
+#undef MSCHAP_SUPPORT
+#define MSCHAP_SUPPORT 1
+/* MSCHAP requires CHAP support */
+#undef CHAP_SUPPORT
+#define CHAP_SUPPORT 1
+#endif /* MPPE_SUPPORT */
+
+/**
+ * CBCP_SUPPORT==1: Support CBCP. CURRENTLY NOT SUPPORTED! DO NOT SET!
+ */
+#ifndef CBCP_SUPPORT
+#define CBCP_SUPPORT 0
+#endif
+
+/**
+ * ECP_SUPPORT==1: Support ECP. CURRENTLY NOT SUPPORTED! DO NOT SET!
+ */
+#ifndef ECP_SUPPORT
+#define ECP_SUPPORT 0
+#endif
+
+/**
+ * DEMAND_SUPPORT==1: Support dial on demand. CURRENTLY NOT SUPPORTED! DO NOT SET!
+ */
+#ifndef DEMAND_SUPPORT
+#define DEMAND_SUPPORT 0
+#endif
+
+/**
+ * LQR_SUPPORT==1: Support Link Quality Report. Do nothing except exchanging some LCP packets.
+ */
+#ifndef LQR_SUPPORT
+#define LQR_SUPPORT 0
+#endif
+
+/**
+ * PPP_SERVER==1: Enable PPP server support (waiting for incoming PPP session).
+ *
+ * Currently only supported for PPPoS.
+ */
+#ifndef PPP_SERVER
+#define PPP_SERVER 0
+#endif
+
+#if PPP_SERVER
+/*
+ * PPP_OUR_NAME: Our name for authentication purposes
+ */
+#ifndef PPP_OUR_NAME
+#define PPP_OUR_NAME "lwIP"
+#endif
+#endif /* PPP_SERVER */
+
+/**
+ * VJ_SUPPORT==1: Support VJ header compression.
+ */
+#ifndef VJ_SUPPORT
+#define VJ_SUPPORT 1
+#endif
+/* VJ compression is only supported for TCP over IPv4 over PPPoS. */
+#if !PPPOS_SUPPORT || !PPP_IPV4_SUPPORT || !LWIP_TCP
+#undef VJ_SUPPORT
+#define VJ_SUPPORT 0
+#endif /* !PPPOS_SUPPORT */
+
+/**
+ * PPP_MD5_RANDM==1: Use MD5 for better randomness.
+ * Enabled by default if CHAP, EAP, or L2TP AUTH support is enabled.
+ */
+#ifndef PPP_MD5_RANDM
+#define PPP_MD5_RANDM (CHAP_SUPPORT || EAP_SUPPORT || PPPOL2TP_AUTH_SUPPORT)
+#endif
+
+/**
+ * PolarSSL embedded library
+ *
+ *
+ * lwIP contains some files fetched from the latest BSD release of
+ * the PolarSSL project (PolarSSL 0.10.1-bsd) for ciphers and encryption
+ * methods we need for lwIP PPP support.
+ *
+ * The PolarSSL files were cleaned to contain only the necessary struct
+ * fields and functions needed for lwIP.
+ *
+ * The PolarSSL API was not changed at all, so if you are already using
+ * PolarSSL you can choose to skip the compilation of the included PolarSSL
+ * library into lwIP.
+ *
+ * If you are not using the embedded copy you must include external
+ * libraries into your arch/cc.h port file.
+ *
+ * Beware of the stack requirements which can be a lot larger if you are not
+ * using our cleaned PolarSSL library.
+ */
+
+/**
+ * LWIP_USE_EXTERNAL_POLARSSL: Use external PolarSSL library
+ */
+#ifndef LWIP_USE_EXTERNAL_POLARSSL
+#define LWIP_USE_EXTERNAL_POLARSSL 0
+#endif
+
+/**
+ * LWIP_USE_EXTERNAL_MBEDTLS: Use external mbed TLS library
+ */
+#ifndef LWIP_USE_EXTERNAL_MBEDTLS
+#define LWIP_USE_EXTERNAL_MBEDTLS 0
+#endif
+
+/*
+ * PPP Timeouts
+ */
+
+/**
+ * FSM_DEFTIMEOUT: Timeout time in seconds
+ */
+#ifndef FSM_DEFTIMEOUT
+#define FSM_DEFTIMEOUT 6
+#endif
+
+/**
+ * FSM_DEFMAXTERMREQS: Maximum Terminate-Request transmissions
+ */
+#ifndef FSM_DEFMAXTERMREQS
+#define FSM_DEFMAXTERMREQS 2
+#endif
+
+/**
+ * FSM_DEFMAXCONFREQS: Maximum Configure-Request transmissions
+ */
+#ifndef FSM_DEFMAXCONFREQS
+#define FSM_DEFMAXCONFREQS 10
+#endif
+
+/**
+ * FSM_DEFMAXNAKLOOPS: Maximum number of nak loops
+ */
+#ifndef FSM_DEFMAXNAKLOOPS
+#define FSM_DEFMAXNAKLOOPS 5
+#endif
+
+/**
+ * UPAP_DEFTIMEOUT: Timeout (seconds) for retransmitting req
+ */
+#ifndef UPAP_DEFTIMEOUT
+#define UPAP_DEFTIMEOUT 6
+#endif
+
+/**
+ * UPAP_DEFTRANSMITS: Maximum number of auth-reqs to send
+ */
+#ifndef UPAP_DEFTRANSMITS
+#define UPAP_DEFTRANSMITS 10
+#endif
+
+#if PPP_SERVER
+/**
+ * UPAP_DEFREQTIME: Time to wait for auth-req from peer
+ */
+#ifndef UPAP_DEFREQTIME
+#define UPAP_DEFREQTIME 30
+#endif
+#endif /* PPP_SERVER */
+
+/**
+ * CHAP_DEFTIMEOUT: Timeout (seconds) for retransmitting req
+ */
+#ifndef CHAP_DEFTIMEOUT
+#define CHAP_DEFTIMEOUT 6
+#endif
+
+/**
+ * CHAP_DEFTRANSMITS: max # times to send challenge
+ */
+#ifndef CHAP_DEFTRANSMITS
+#define CHAP_DEFTRANSMITS 10
+#endif
+
+#if PPP_SERVER
+/**
+ * CHAP_DEFRECHALLENGETIME: If this option is > 0, rechallenge the peer every n seconds
+ */
+#ifndef CHAP_DEFRECHALLENGETIME
+#define CHAP_DEFRECHALLENGETIME 0
+#endif
+#endif /* PPP_SERVER */
+
+/**
+ * EAP_DEFREQTIME: Time to wait for peer request
+ */
+#ifndef EAP_DEFREQTIME
+#define EAP_DEFREQTIME 6
+#endif
+
+/**
+ * EAP_DEFALLOWREQ: max # times to accept requests
+ */
+#ifndef EAP_DEFALLOWREQ
+#define EAP_DEFALLOWREQ 10
+#endif
+
+#if PPP_SERVER
+/**
+ * EAP_DEFTIMEOUT: Timeout (seconds) for rexmit
+ */
+#ifndef EAP_DEFTIMEOUT
+#define EAP_DEFTIMEOUT 6
+#endif
+
+/**
+ * EAP_DEFTRANSMITS: max # times to transmit
+ */
+#ifndef EAP_DEFTRANSMITS
+#define EAP_DEFTRANSMITS 10
+#endif
+#endif /* PPP_SERVER */
+
+/**
+ * LCP_DEFLOOPBACKFAIL: Default number of times we receive our magic number from the peer
+ * before deciding the link is looped-back.
+ */
+#ifndef LCP_DEFLOOPBACKFAIL
+#define LCP_DEFLOOPBACKFAIL 10
+#endif
+
+/**
+ * LCP_ECHOINTERVAL: Interval in seconds between keepalive echo requests, 0 to disable.
+ */
+#ifndef LCP_ECHOINTERVAL
+#define LCP_ECHOINTERVAL 0
+#endif
+
+/**
+ * LCP_MAXECHOFAILS: Number of unanswered echo requests before failure.
+ */
+#ifndef LCP_MAXECHOFAILS
+#define LCP_MAXECHOFAILS 3
+#endif
+
+/**
+ * PPP_MAXIDLEFLAG: Max Xmit idle time (in ms) before resend flag char.
+ */
+#ifndef PPP_MAXIDLEFLAG
+#define PPP_MAXIDLEFLAG 100
+#endif
+
+/**
+ * PPP Packet sizes
+ */
+
+/**
+ * PPP_MRU: Default MRU
+ */
+#ifndef PPP_MRU
+#define PPP_MRU 1500
+#endif
+
+/**
+ * PPP_DEFMRU: Default MRU to try
+ */
+#ifndef PPP_DEFMRU
+#define PPP_DEFMRU 1500
+#endif
+
+/**
+ * PPP_MAXMRU: Normally limit MRU to this (pppd default = 16384)
+ */
+#ifndef PPP_MAXMRU
+#define PPP_MAXMRU 1500
+#endif
+
+/**
+ * PPP_MINMRU: No MRUs below this
+ */
+#ifndef PPP_MINMRU
+#define PPP_MINMRU 128
+#endif
+
+/**
+ * PPPOL2TP_DEFMRU: Default MTU and MRU for L2TP
+ * Default = 1500 - PPPoE(6) - PPP Protocol(2) - IPv4 header(20) - UDP Header(8)
+ * - L2TP Header(6) - HDLC Header(2) - PPP Protocol(2) - MPPE Header(2) - PPP Protocol(2)
+ */
+#if PPPOL2TP_SUPPORT
+#ifndef PPPOL2TP_DEFMRU
+#define PPPOL2TP_DEFMRU 1450
+#endif
+#endif /* PPPOL2TP_SUPPORT */
+
+/**
+ * MAXNAMELEN: max length of hostname or name for auth
+ */
+#ifndef MAXNAMELEN
+#define MAXNAMELEN 256
+#endif
+
+/**
+ * MAXSECRETLEN: max length of password or secret
+ */
+#ifndef MAXSECRETLEN
+#define MAXSECRETLEN 256
+#endif
+
+/* ------------------------------------------------------------------------- */
+
+/*
+ * Build triggers for embedded PolarSSL
+ */
+#if !LWIP_USE_EXTERNAL_POLARSSL && !LWIP_USE_EXTERNAL_MBEDTLS
+
+/* CHAP, EAP, L2TP AUTH and MD5 Random require MD5 support */
+#if CHAP_SUPPORT || EAP_SUPPORT || PPPOL2TP_AUTH_SUPPORT || PPP_MD5_RANDM
+#define LWIP_INCLUDED_POLARSSL_MD5 1
+#endif /* CHAP_SUPPORT || EAP_SUPPORT || PPPOL2TP_AUTH_SUPPORT || PPP_MD5_RANDM */
+
+#if MSCHAP_SUPPORT
+
+/* MSCHAP require MD4 support */
+#define LWIP_INCLUDED_POLARSSL_MD4 1
+/* MSCHAP require SHA1 support */
+#define LWIP_INCLUDED_POLARSSL_SHA1 1
+/* MSCHAP require DES support */
+#define LWIP_INCLUDED_POLARSSL_DES 1
+
+/* MS-CHAP support is required for MPPE */
+#if MPPE_SUPPORT
+/* MPPE require ARC4 support */
+#define LWIP_INCLUDED_POLARSSL_ARC4 1
+#endif /* MPPE_SUPPORT */
+
+#endif /* MSCHAP_SUPPORT */
+
+#endif /* !LWIP_USE_EXTERNAL_POLARSSL && !LWIP_USE_EXTERNAL_MBEDTLS */
+
+/* Default value if unset */
+#ifndef LWIP_INCLUDED_POLARSSL_MD4
+#define LWIP_INCLUDED_POLARSSL_MD4 0
+#endif /* LWIP_INCLUDED_POLARSSL_MD4 */
+#ifndef LWIP_INCLUDED_POLARSSL_MD5
+#define LWIP_INCLUDED_POLARSSL_MD5 0
+#endif /* LWIP_INCLUDED_POLARSSL_MD5 */
+#ifndef LWIP_INCLUDED_POLARSSL_SHA1
+#define LWIP_INCLUDED_POLARSSL_SHA1 0
+#endif /* LWIP_INCLUDED_POLARSSL_SHA1 */
+#ifndef LWIP_INCLUDED_POLARSSL_DES
+#define LWIP_INCLUDED_POLARSSL_DES 0
+#endif /* LWIP_INCLUDED_POLARSSL_DES */
+#ifndef LWIP_INCLUDED_POLARSSL_ARC4
+#define LWIP_INCLUDED_POLARSSL_ARC4 0
+#endif /* LWIP_INCLUDED_POLARSSL_ARC4 */
+
+#endif /* PPP_SUPPORT */
+
+#endif /* LWIP_PPP_OPTS_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/pppapi.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/pppapi.h
new file mode 100644
index 0000000..913d93f
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/pppapi.h
@@ -0,0 +1,137 @@
+/*
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ */
+
+#ifndef LWIP_PPPAPI_H
+#define LWIP_PPPAPI_H
+
+#include "netif/ppp/ppp_opts.h"
+
+#if LWIP_PPP_API /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/sys.h"
+#include "lwip/netif.h"
+#include "lwip/priv/tcpip_priv.h"
+#include "netif/ppp/ppp.h"
+#if PPPOS_SUPPORT
+#include "netif/ppp/pppos.h"
+#endif /* PPPOS_SUPPORT */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct pppapi_msg_msg {
+ ppp_pcb *ppp;
+ union {
+#if PPP_NOTIFY_PHASE
+ struct {
+ ppp_notify_phase_cb_fn notify_phase_cb;
+ } setnotifyphasecb;
+#endif /* PPP_NOTIFY_PHASE */
+#if PPPOS_SUPPORT
+ struct {
+ struct netif *pppif;
+ pppos_output_cb_fn output_cb;
+ ppp_link_status_cb_fn link_status_cb;
+ void *ctx_cb;
+ } serialcreate;
+#endif /* PPPOS_SUPPORT */
+#if PPPOE_SUPPORT
+ struct {
+ struct netif *pppif;
+ struct netif *ethif;
+ const char *service_name;
+ const char *concentrator_name;
+ ppp_link_status_cb_fn link_status_cb;
+ void *ctx_cb;
+ } ethernetcreate;
+#endif /* PPPOE_SUPPORT */
+#if PPPOL2TP_SUPPORT
+ struct {
+ struct netif *pppif;
+ struct netif *netif;
+ API_MSG_M_DEF_C(ip_addr_t, ipaddr);
+ u16_t port;
+#if PPPOL2TP_AUTH_SUPPORT
+ const u8_t *secret;
+ u8_t secret_len;
+#endif /* PPPOL2TP_AUTH_SUPPORT */
+ ppp_link_status_cb_fn link_status_cb;
+ void *ctx_cb;
+ } l2tpcreate;
+#endif /* PPPOL2TP_SUPPORT */
+ struct {
+ u16_t holdoff;
+ } connect;
+ struct {
+ u8_t nocarrier;
+ } close;
+ struct {
+ u8_t cmd;
+ void *arg;
+ } ioctl;
+ } msg;
+};
+
+struct pppapi_msg {
+ struct tcpip_api_call_data call;
+ struct pppapi_msg_msg msg;
+};
+
+/* API for application */
+err_t pppapi_set_default(ppp_pcb *pcb);
+#if PPP_NOTIFY_PHASE
+err_t pppapi_set_notify_phase_callback(ppp_pcb *pcb, ppp_notify_phase_cb_fn notify_phase_cb);
+#endif /* PPP_NOTIFY_PHASE */
+#if PPPOS_SUPPORT
+ppp_pcb *pppapi_pppos_create(struct netif *pppif, pppos_output_cb_fn output_cb, ppp_link_status_cb_fn link_status_cb, void *ctx_cb);
+#endif /* PPPOS_SUPPORT */
+#if PPPOE_SUPPORT
+ppp_pcb *pppapi_pppoe_create(struct netif *pppif, struct netif *ethif, const char *service_name,
+ const char *concentrator_name, ppp_link_status_cb_fn link_status_cb,
+ void *ctx_cb);
+#endif /* PPPOE_SUPPORT */
+#if PPPOL2TP_SUPPORT
+ppp_pcb *pppapi_pppol2tp_create(struct netif *pppif, struct netif *netif, ip_addr_t *ipaddr, u16_t port,
+ const u8_t *secret, u8_t secret_len,
+ ppp_link_status_cb_fn link_status_cb, void *ctx_cb);
+#endif /* PPPOL2TP_SUPPORT */
+err_t pppapi_connect(ppp_pcb *pcb, u16_t holdoff);
+#if PPP_SERVER
+err_t pppapi_listen(ppp_pcb *pcb);
+#endif /* PPP_SERVER */
+err_t pppapi_close(ppp_pcb *pcb, u8_t nocarrier);
+err_t pppapi_free(ppp_pcb *pcb);
+err_t pppapi_ioctl(ppp_pcb *pcb, u8_t cmd, void *arg);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_PPP_API */
+
+#endif /* LWIP_PPPAPI_H */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/pppcrypt.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/pppcrypt.h
new file mode 100644
index 0000000..a7b2099
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/pppcrypt.h
@@ -0,0 +1,136 @@
+/*
+ * pppcrypt.c - PPP/DES linkage for MS-CHAP and EAP SRP-SHA1
+ *
+ * Extracted from chap_ms.c by James Carlson.
+ *
+ * Copyright (c) 1995 Eric Rosenquist. 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(s) of the authors of this software must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission.
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+/* This header file is included in all PPP modules needing hashes and/or ciphers */
+
+#ifndef PPPCRYPT_H
+#define PPPCRYPT_H
+
+/*
+ * If included PolarSSL copy is not used, user is expected to include
+ * external libraries in arch/cc.h (which is included by lwip/arch.h).
+ */
+#include "lwip/arch.h"
+
+/*
+ * Map hashes and ciphers functions to PolarSSL
+ */
+#if !LWIP_USE_EXTERNAL_MBEDTLS
+
+#include "netif/ppp/polarssl/md4.h"
+#define lwip_md4_context md4_context
+#define lwip_md4_init(context)
+#define lwip_md4_starts md4_starts
+#define lwip_md4_update md4_update
+#define lwip_md4_finish md4_finish
+#define lwip_md4_free(context)
+
+#include "netif/ppp/polarssl/md5.h"
+#define lwip_md5_context md5_context
+#define lwip_md5_init(context)
+#define lwip_md5_starts md5_starts
+#define lwip_md5_update md5_update
+#define lwip_md5_finish md5_finish
+#define lwip_md5_free(context)
+
+#include "netif/ppp/polarssl/sha1.h"
+#define lwip_sha1_context sha1_context
+#define lwip_sha1_init(context)
+#define lwip_sha1_starts sha1_starts
+#define lwip_sha1_update sha1_update
+#define lwip_sha1_finish sha1_finish
+#define lwip_sha1_free(context)
+
+#include "netif/ppp/polarssl/des.h"
+#define lwip_des_context des_context
+#define lwip_des_init(context)
+#define lwip_des_setkey_enc des_setkey_enc
+#define lwip_des_crypt_ecb des_crypt_ecb
+#define lwip_des_free(context)
+
+#include "netif/ppp/polarssl/arc4.h"
+#define lwip_arc4_context arc4_context
+#define lwip_arc4_init(context)
+#define lwip_arc4_setup arc4_setup
+#define lwip_arc4_crypt arc4_crypt
+#define lwip_arc4_free(context)
+
+#endif /* !LWIP_USE_EXTERNAL_MBEDTLS */
+
+/*
+ * Map hashes and ciphers functions to mbed TLS
+ */
+#if LWIP_USE_EXTERNAL_MBEDTLS
+
+#define lwip_md4_context mbedtls_md4_context
+#define lwip_md4_init mbedtls_md4_init
+#define lwip_md4_starts mbedtls_md4_starts
+#define lwip_md4_update mbedtls_md4_update
+#define lwip_md4_finish mbedtls_md4_finish
+#define lwip_md4_free mbedtls_md4_free
+
+#define lwip_md5_context mbedtls_md5_context
+#define lwip_md5_init mbedtls_md5_init
+#define lwip_md5_starts mbedtls_md5_starts
+#define lwip_md5_update mbedtls_md5_update
+#define lwip_md5_finish mbedtls_md5_finish
+#define lwip_md5_free mbedtls_md5_free
+
+#define lwip_sha1_context mbedtls_sha1_context
+#define lwip_sha1_init mbedtls_sha1_init
+#define lwip_sha1_starts mbedtls_sha1_starts
+#define lwip_sha1_update mbedtls_sha1_update
+#define lwip_sha1_finish mbedtls_sha1_finish
+#define lwip_sha1_free mbedtls_sha1_free
+
+#define lwip_des_context mbedtls_des_context
+#define lwip_des_init mbedtls_des_init
+#define lwip_des_setkey_enc mbedtls_des_setkey_enc
+#define lwip_des_crypt_ecb mbedtls_des_crypt_ecb
+#define lwip_des_free mbedtls_des_free
+
+#define lwip_arc4_context mbedtls_arc4_context
+#define lwip_arc4_init mbedtls_arc4_init
+#define lwip_arc4_setup mbedtls_arc4_setup
+#define lwip_arc4_crypt(context, buffer, length) mbedtls_arc4_crypt(context, length, buffer, buffer)
+#define lwip_arc4_free mbedtls_arc4_free
+
+#endif /* LWIP_USE_EXTERNAL_MBEDTLS */
+
+void pppcrypt_56_to_64_bit_key(u_char *key, u_char *des_key);
+
+#endif /* PPPCRYPT_H */
+
+#endif /* PPP_SUPPORT */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/pppdebug.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/pppdebug.h
new file mode 100644
index 0000000..7ead045
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/pppdebug.h
@@ -0,0 +1,80 @@
+/*****************************************************************************
+* 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.
+*
+*****************************************************************************
+*/
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#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)
+
+#if PPP_DEBUG
+
+#define MAINDEBUG(a) LWIP_DEBUGF(LWIP_DBG_LEVEL_WARNING, a)
+#define SYSDEBUG(a) LWIP_DEBUGF(LWIP_DBG_LEVEL_WARNING, a)
+#define FSMDEBUG(a) LWIP_DEBUGF(LWIP_DBG_LEVEL_WARNING, a)
+#define LCPDEBUG(a) LWIP_DEBUGF(LWIP_DBG_LEVEL_WARNING, a)
+#define IPCPDEBUG(a) LWIP_DEBUGF(LWIP_DBG_LEVEL_WARNING, a)
+#define IPV6CPDEBUG(a) LWIP_DEBUGF(LWIP_DBG_LEVEL_WARNING, a)
+#define UPAPDEBUG(a) LWIP_DEBUGF(LWIP_DBG_LEVEL_WARNING, a)
+#define CHAPDEBUG(a) LWIP_DEBUGF(LWIP_DBG_LEVEL_WARNING, a)
+#define PPPDEBUG(a, b) LWIP_DEBUGF(a, b)
+
+#else /* PPP_DEBUG */
+
+#define MAINDEBUG(a)
+#define SYSDEBUG(a)
+#define FSMDEBUG(a)
+#define LCPDEBUG(a)
+#define IPCPDEBUG(a)
+#define IPV6CPDEBUG(a)
+#define UPAPDEBUG(a)
+#define CHAPDEBUG(a)
+#define PPPDEBUG(a, b)
+
+#endif /* PPP_DEBUG */
+
+#endif /* PPPDEBUG_H */
+
+#endif /* PPP_SUPPORT */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/pppoe.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/pppoe.h
new file mode 100644
index 0000000..9f8f289
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/pppoe.h
@@ -0,0 +1,179 @@
+/*****************************************************************************
+* pppoe.h - PPP Over Ethernet implementation for lwIP.
+*
+* Copyright (c) 2006 by Marc Boucher, Services Informatiques (MBSI) inc.
+*
+* The authors hereby grant permission to use, copy, modify, distribute,
+* and license this software and its documentation for any purpose, provided
+* that existing copyright notices are retained in all copies and that this
+* notice and the following disclaimer are included verbatim in any
+* distributions. No written agreement, license, or royalty fee is required
+* for any of the authorized uses.
+*
+* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+******************************************************************************
+* REVISION HISTORY
+*
+* 06-01-01 Marc Boucher <marc@mbsi.ca>
+* Ported to lwIP.
+*****************************************************************************/
+
+
+
+/* based on NetBSD: if_pppoe.c,v 1.64 2006/01/31 23:50:15 martin Exp */
+
+/*-
+ * Copyright (c) 2002 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Martin Husemann <martin@NetBSD.org>.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT && PPPOE_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#ifndef PPP_OE_H
+#define PPP_OE_H
+
+#include "ppp.h"
+#include "lwip/etharp.h"
+
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct pppoehdr {
+ PACK_STRUCT_FLD_8(u8_t vertype);
+ PACK_STRUCT_FLD_8(u8_t code);
+ PACK_STRUCT_FIELD(u16_t session);
+ PACK_STRUCT_FIELD(u16_t plen);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct pppoetag {
+ PACK_STRUCT_FIELD(u16_t tag);
+ PACK_STRUCT_FIELD(u16_t len);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+
+#define PPPOE_STATE_INITIAL 0
+#define PPPOE_STATE_PADI_SENT 1
+#define PPPOE_STATE_PADR_SENT 2
+#define PPPOE_STATE_SESSION 3
+/* passive */
+#define PPPOE_STATE_PADO_SENT 1
+
+#define PPPOE_HEADERLEN sizeof(struct pppoehdr)
+#define PPPOE_VERTYPE 0x11 /* VER=1, TYPE = 1 */
+
+#define PPPOE_TAG_EOL 0x0000 /* end of list */
+#define PPPOE_TAG_SNAME 0x0101 /* service name */
+#define PPPOE_TAG_ACNAME 0x0102 /* access concentrator name */
+#define PPPOE_TAG_HUNIQUE 0x0103 /* host unique */
+#define PPPOE_TAG_ACCOOKIE 0x0104 /* AC cookie */
+#define PPPOE_TAG_VENDOR 0x0105 /* vendor specific */
+#define PPPOE_TAG_RELAYSID 0x0110 /* relay session id */
+#define PPPOE_TAG_SNAME_ERR 0x0201 /* service name error */
+#define PPPOE_TAG_ACSYS_ERR 0x0202 /* AC system error */
+#define PPPOE_TAG_GENERIC_ERR 0x0203 /* gerneric error */
+
+#define PPPOE_CODE_PADI 0x09 /* Active Discovery Initiation */
+#define PPPOE_CODE_PADO 0x07 /* Active Discovery Offer */
+#define PPPOE_CODE_PADR 0x19 /* Active Discovery Request */
+#define PPPOE_CODE_PADS 0x65 /* Active Discovery Session confirmation */
+#define PPPOE_CODE_PADT 0xA7 /* Active Discovery Terminate */
+
+#ifndef PPPOE_MAX_AC_COOKIE_LEN
+#define PPPOE_MAX_AC_COOKIE_LEN 64
+#endif
+
+struct pppoe_softc {
+ struct pppoe_softc *next;
+ struct netif *sc_ethif; /* ethernet interface we are using */
+ ppp_pcb *pcb; /* PPP PCB */
+
+ struct eth_addr sc_dest; /* hardware address of concentrator */
+ u16_t sc_session; /* PPPoE session id */
+ u8_t sc_state; /* discovery phase or session connected */
+
+#ifdef PPPOE_TODO
+ u8_t *sc_service_name; /* if != NULL: requested name of service */
+ u8_t *sc_concentrator_name; /* if != NULL: requested concentrator id */
+#endif /* PPPOE_TODO */
+ u8_t sc_ac_cookie[PPPOE_MAX_AC_COOKIE_LEN]; /* content of AC cookie we must echo back */
+ u8_t sc_ac_cookie_len; /* length of cookie data */
+#ifdef PPPOE_SERVER
+ u8_t *sc_hunique; /* content of host unique we must echo back */
+ u8_t sc_hunique_len; /* length of host unique */
+#endif
+ u8_t sc_padi_retried; /* number of PADI retries already done */
+ u8_t sc_padr_retried; /* number of PADR retries already done */
+};
+
+
+#define pppoe_init() /* compatibility define, no initialization needed */
+
+ppp_pcb *pppoe_create(struct netif *pppif,
+ struct netif *ethif,
+ const char *service_name, const char *concentrator_name,
+ ppp_link_status_cb_fn link_status_cb, void *ctx_cb);
+
+/*
+ * Functions called from lwIP
+ * DO NOT CALL FROM lwIP USER APPLICATION.
+ */
+void pppoe_disc_input(struct netif *netif, struct pbuf *p);
+void pppoe_data_input(struct netif *netif, struct pbuf *p);
+
+#endif /* PPP_OE_H */
+
+#endif /* PPP_SUPPORT && PPPOE_SUPPORT */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/pppol2tp.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/pppol2tp.h
new file mode 100644
index 0000000..f03950e
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/pppol2tp.h
@@ -0,0 +1,201 @@
+/**
+ * @file
+ * Network Point to Point Protocol over Layer 2 Tunneling Protocol header file.
+ *
+ */
+
+/*
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT && PPPOL2TP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#ifndef PPPOL2TP_H
+#define PPPOL2TP_H
+
+#include "ppp.h"
+
+/* Timeout */
+#define PPPOL2TP_CONTROL_TIMEOUT (5*1000) /* base for quick timeout calculation */
+#define PPPOL2TP_SLOW_RETRY (60*1000) /* persistent retry interval */
+
+#define PPPOL2TP_MAXSCCRQ 4 /* retry SCCRQ four times (quickly) */
+#define PPPOL2TP_MAXICRQ 4 /* retry IRCQ four times */
+#define PPPOL2TP_MAXICCN 4 /* retry ICCN four times */
+
+/* L2TP header flags */
+#define PPPOL2TP_HEADERFLAG_CONTROL 0x8000
+#define PPPOL2TP_HEADERFLAG_LENGTH 0x4000
+#define PPPOL2TP_HEADERFLAG_SEQUENCE 0x0800
+#define PPPOL2TP_HEADERFLAG_OFFSET 0x0200
+#define PPPOL2TP_HEADERFLAG_PRIORITY 0x0100
+#define PPPOL2TP_HEADERFLAG_VERSION 0x0002
+
+/* Mandatory bits for control: Control, Length, Sequence, Version 2 */
+#define PPPOL2TP_HEADERFLAG_CONTROL_MANDATORY (PPPOL2TP_HEADERFLAG_CONTROL|PPPOL2TP_HEADERFLAG_LENGTH|PPPOL2TP_HEADERFLAG_SEQUENCE|PPPOL2TP_HEADERFLAG_VERSION)
+/* Forbidden bits for control: Offset, Priority */
+#define PPPOL2TP_HEADERFLAG_CONTROL_FORBIDDEN (PPPOL2TP_HEADERFLAG_OFFSET|PPPOL2TP_HEADERFLAG_PRIORITY)
+
+/* Mandatory bits for data: Version 2 */
+#define PPPOL2TP_HEADERFLAG_DATA_MANDATORY (PPPOL2TP_HEADERFLAG_VERSION)
+
+/* AVP (Attribute Value Pair) header */
+#define PPPOL2TP_AVPHEADERFLAG_MANDATORY 0x8000
+#define PPPOL2TP_AVPHEADERFLAG_HIDDEN 0x4000
+#define PPPOL2TP_AVPHEADERFLAG_LENGTHMASK 0x03ff
+
+/* -- AVP - Message type */
+#define PPPOL2TP_AVPTYPE_MESSAGE 0 /* Message type */
+
+/* Control Connection Management */
+#define PPPOL2TP_MESSAGETYPE_SCCRQ 1 /* Start Control Connection Request */
+#define PPPOL2TP_MESSAGETYPE_SCCRP 2 /* Start Control Connection Reply */
+#define PPPOL2TP_MESSAGETYPE_SCCCN 3 /* Start Control Connection Connected */
+#define PPPOL2TP_MESSAGETYPE_STOPCCN 4 /* Stop Control Connection Notification */
+#define PPPOL2TP_MESSAGETYPE_HELLO 6 /* Hello */
+/* Call Management */
+#define PPPOL2TP_MESSAGETYPE_OCRQ 7 /* Outgoing Call Request */
+#define PPPOL2TP_MESSAGETYPE_OCRP 8 /* Outgoing Call Reply */
+#define PPPOL2TP_MESSAGETYPE_OCCN 9 /* Outgoing Call Connected */
+#define PPPOL2TP_MESSAGETYPE_ICRQ 10 /* Incoming Call Request */
+#define PPPOL2TP_MESSAGETYPE_ICRP 11 /* Incoming Call Reply */
+#define PPPOL2TP_MESSAGETYPE_ICCN 12 /* Incoming Call Connected */
+#define PPPOL2TP_MESSAGETYPE_CDN 14 /* Call Disconnect Notify */
+/* Error reporting */
+#define PPPOL2TP_MESSAGETYPE_WEN 15 /* WAN Error Notify */
+/* PPP Session Control */
+#define PPPOL2TP_MESSAGETYPE_SLI 16 /* Set Link Info */
+
+/* -- AVP - Result code */
+#define PPPOL2TP_AVPTYPE_RESULTCODE 1 /* Result code */
+#define PPPOL2TP_RESULTCODE 1 /* General request to clear control connection */
+
+/* -- AVP - Protocol version (!= L2TP Header version) */
+#define PPPOL2TP_AVPTYPE_VERSION 2
+#define PPPOL2TP_VERSION 0x0100 /* L2TP Protocol version 1, revision 0 */
+
+/* -- AVP - Framing capabilities */
+#define PPPOL2TP_AVPTYPE_FRAMINGCAPABILITIES 3 /* Bearer capabilities */
+#define PPPOL2TP_FRAMINGCAPABILITIES 0x00000003 /* Async + Sync framing */
+
+/* -- AVP - Bearer capabilities */
+#define PPPOL2TP_AVPTYPE_BEARERCAPABILITIES 4 /* Bearer capabilities */
+#define PPPOL2TP_BEARERCAPABILITIES 0x00000003 /* Analog + Digital Access */
+
+/* -- AVP - Tie breaker */
+#define PPPOL2TP_AVPTYPE_TIEBREAKER 5
+
+/* -- AVP - Host name */
+#define PPPOL2TP_AVPTYPE_HOSTNAME 7 /* Host name */
+#define PPPOL2TP_HOSTNAME "lwIP" /* FIXME: make it configurable */
+
+/* -- AVP - Vendor name */
+#define PPPOL2TP_AVPTYPE_VENDORNAME 8 /* Vendor name */
+#define PPPOL2TP_VENDORNAME "lwIP" /* FIXME: make it configurable */
+
+/* -- AVP - Assign tunnel ID */
+#define PPPOL2TP_AVPTYPE_TUNNELID 9 /* Assign Tunnel ID */
+
+/* -- AVP - Receive window size */
+#define PPPOL2TP_AVPTYPE_RECEIVEWINDOWSIZE 10 /* Receive window size */
+#define PPPOL2TP_RECEIVEWINDOWSIZE 8 /* FIXME: make it configurable */
+
+/* -- AVP - Challenge */
+#define PPPOL2TP_AVPTYPE_CHALLENGE 11 /* Challenge */
+
+/* -- AVP - Cause code */
+#define PPPOL2TP_AVPTYPE_CAUSECODE 12 /* Cause code*/
+
+/* -- AVP - Challenge response */
+#define PPPOL2TP_AVPTYPE_CHALLENGERESPONSE 13 /* Challenge response */
+#define PPPOL2TP_AVPTYPE_CHALLENGERESPONSE_SIZE 16
+
+/* -- AVP - Assign session ID */
+#define PPPOL2TP_AVPTYPE_SESSIONID 14 /* Assign Session ID */
+
+/* -- AVP - Call serial number */
+#define PPPOL2TP_AVPTYPE_CALLSERIALNUMBER 15 /* Call Serial Number */
+
+/* -- AVP - Framing type */
+#define PPPOL2TP_AVPTYPE_FRAMINGTYPE 19 /* Framing Type */
+#define PPPOL2TP_FRAMINGTYPE 0x00000001 /* Sync framing */
+
+/* -- AVP - TX Connect Speed */
+#define PPPOL2TP_AVPTYPE_TXCONNECTSPEED 24 /* TX Connect Speed */
+#define PPPOL2TP_TXCONNECTSPEED 100000000 /* Connect speed: 100 Mbits/s */
+
+/* L2TP Session state */
+#define PPPOL2TP_STATE_INITIAL 0
+#define PPPOL2TP_STATE_SCCRQ_SENT 1
+#define PPPOL2TP_STATE_ICRQ_SENT 2
+#define PPPOL2TP_STATE_ICCN_SENT 3
+#define PPPOL2TP_STATE_DATA 4
+
+#define PPPOL2TP_OUTPUT_DATA_HEADER_LEN 6 /* Our data header len */
+
+/*
+ * PPPoL2TP interface control block.
+ */
+typedef struct pppol2tp_pcb_s pppol2tp_pcb;
+struct pppol2tp_pcb_s {
+ ppp_pcb *ppp; /* PPP PCB */
+ u8_t phase; /* L2TP phase */
+ struct udp_pcb *udp; /* UDP L2TP Socket */
+ struct netif *netif; /* Output interface, used as a default route */
+ ip_addr_t remote_ip; /* LNS IP Address */
+ u16_t remote_port; /* LNS port */
+#if PPPOL2TP_AUTH_SUPPORT
+ const u8_t *secret; /* Secret string */
+ u8_t secret_len; /* Secret string length */
+ u8_t secret_rv[16]; /* Random vector */
+ u8_t challenge_hash[16]; /* Challenge response */
+ u8_t send_challenge; /* Boolean whether the next sent packet should contains a challenge response */
+#endif /* PPPOL2TP_AUTH_SUPPORT */
+
+ u16_t tunnel_port; /* Tunnel port */
+ u16_t our_ns; /* NS to peer */
+ u16_t peer_nr; /* NR from peer */
+ u16_t peer_ns; /* NS from peer */
+ u16_t source_tunnel_id; /* Tunnel ID assigned by peer */
+ u16_t remote_tunnel_id; /* Tunnel ID assigned to peer */
+ u16_t source_session_id; /* Session ID assigned by peer */
+ u16_t remote_session_id; /* Session ID assigned to peer */
+
+ u8_t sccrq_retried; /* number of SCCRQ retries already done */
+ u8_t icrq_retried; /* number of ICRQ retries already done */
+ u8_t iccn_retried; /* number of ICCN retries already done */
+};
+
+
+/* Create a new L2TP session. */
+ppp_pcb *pppol2tp_create(struct netif *pppif,
+ struct netif *netif, const ip_addr_t *ipaddr, u16_t port,
+ const u8_t *secret, u8_t secret_len,
+ ppp_link_status_cb_fn link_status_cb, void *ctx_cb);
+
+#endif /* PPPOL2TP_H */
+#endif /* PPP_SUPPORT && PPPOL2TP_SUPPORT */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/pppos.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/pppos.h
new file mode 100644
index 0000000..d924a9f
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/pppos.h
@@ -0,0 +1,118 @@
+/**
+ * @file
+ * Network Point to Point Protocol over Serial header file.
+ *
+ */
+
+/*
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT && PPPOS_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#ifndef PPPOS_H
+#define PPPOS_H
+
+#include "lwip/sys.h"
+
+#include "ppp.h"
+#include "vj.h"
+
+/* PPP packet parser states. Current state indicates operation yet to be
+ * completed. */
+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. */
+};
+
+/* PPPoS serial output callback function prototype */
+typedef u32_t (*pppos_output_cb_fn)(ppp_pcb *pcb, u8_t *data, u32_t len, void *ctx);
+
+/*
+ * Extended asyncmap - allows any character to be escaped.
+ */
+typedef u8_t ext_accm[32];
+
+/*
+ * PPPoS interface control block.
+ */
+typedef struct pppos_pcb_s pppos_pcb;
+struct pppos_pcb_s {
+ /* -- below are data that will NOT be cleared between two sessions */
+ ppp_pcb *ppp; /* PPP PCB */
+ pppos_output_cb_fn output_cb; /* PPP serial output callback */
+
+ /* -- below are data that will be cleared between two sessions
+ *
+ * last_xmit must be the first member of cleared members, because it is
+ * used to know which part must not be cleared.
+ */
+ u32_t last_xmit; /* Time of last transmission. */
+ ext_accm out_accm; /* Async-Ctl-Char-Map for output. */
+
+ /* flags */
+ unsigned int open :1; /* Set if PPPoS is open */
+ unsigned int pcomp :1; /* Does peer accept protocol compression? */
+ unsigned int accomp :1; /* Does peer accept addr/ctl compression? */
+
+ /* PPPoS rx */
+ ext_accm in_accm; /* Async-Ctl-Char-Map for input. */
+ struct pbuf *in_head, *in_tail; /* The input packet. */
+ u16_t in_protocol; /* The input protocol code. */
+ u16_t in_fcs; /* Input Frame Check Sequence value. */
+ u8_t in_state; /* The input process state. */
+ u8_t in_escaped; /* Escape next character. */
+};
+
+/* Create a new PPPoS session. */
+ppp_pcb *pppos_create(struct netif *pppif, pppos_output_cb_fn output_cb,
+ ppp_link_status_cb_fn link_status_cb, void *ctx_cb);
+
+#if !NO_SYS && !PPP_INPROC_IRQ_SAFE
+/* Pass received raw characters to PPPoS to be decoded through lwIP TCPIP thread. */
+err_t pppos_input_tcpip(ppp_pcb *ppp, u8_t *s, int l);
+#endif /* !NO_SYS && !PPP_INPROC_IRQ_SAFE */
+
+/* PPP over Serial: this is the input function to be called for received data. */
+void pppos_input(ppp_pcb *ppp, u8_t* data, int len);
+
+
+/*
+ * Functions called from lwIP
+ * DO NOT CALL FROM lwIP USER APPLICATION.
+ */
+#if !NO_SYS && !PPP_INPROC_IRQ_SAFE
+err_t pppos_input_sys(struct pbuf *p, struct netif *inp);
+#endif /* !NO_SYS && !PPP_INPROC_IRQ_SAFE */
+
+#endif /* PPPOS_H */
+#endif /* PPP_SUPPORT && PPPOL2TP_SUPPORT */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/upap.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/upap.h
new file mode 100644
index 0000000..7da792e
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/upap.h
@@ -0,0 +1,123 @@
+/*
+ * upap.h - User/Password Authentication Protocol definitions.
+ *
+ * Copyright (c) 1984-2000 Carnegie Mellon University. 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 "Carnegie Mellon University" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission. For permission or any legal
+ * details, please contact
+ * Office of Technology Transfer
+ * Carnegie Mellon University
+ * 5000 Forbes Avenue
+ * Pittsburgh, PA 15213-3890
+ * (412) 268-4387, fax: (412) 268-7395
+ * tech-transfer@andrew.cmu.edu
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by Computing Services
+ * at Carnegie Mellon University (http://www.cmu.edu/computing/)."
+ *
+ * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
+ * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $Id: upap.h,v 1.8 2002/12/04 23:03:33 paulus Exp $
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT && PAP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#ifndef UPAP_H
+#define UPAP_H
+
+#include "ppp.h"
+
+/*
+ * Packet header = Code, id, length.
+ */
+#define UPAP_HEADERLEN 4
+
+
+/*
+ * UPAP codes.
+ */
+#define UPAP_AUTHREQ 1 /* Authenticate-Request */
+#define UPAP_AUTHACK 2 /* Authenticate-Ack */
+#define UPAP_AUTHNAK 3 /* Authenticate-Nak */
+
+
+/*
+ * 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 */
+
+
+/*
+ * Timeouts.
+ */
+#if 0 /* moved to ppp_opts.h */
+#define UPAP_DEFTIMEOUT 3 /* Timeout (seconds) for retransmitting req */
+#define UPAP_DEFREQTIME 30 /* Time to wait for auth-req from peer */
+#endif /* moved to ppp_opts.h */
+
+/*
+ * Each interface is described by upap structure.
+ */
+#if PAP_SUPPORT
+typedef struct upap_state {
+ const char *us_user; /* User */
+ u8_t us_userlen; /* User length */
+ const char *us_passwd; /* Password */
+ u8_t us_passwdlen; /* Password length */
+ u8_t us_clientstate; /* Client state */
+#if PPP_SERVER
+ u8_t us_serverstate; /* Server state */
+#endif /* PPP_SERVER */
+ u8_t us_id; /* Current id */
+ u8_t us_transmits; /* Number of auth-reqs sent */
+} upap_state;
+#endif /* PAP_SUPPORT */
+
+
+void upap_authwithpeer(ppp_pcb *pcb, const char *user, const char *password);
+#if PPP_SERVER
+void upap_authpeer(ppp_pcb *pcb);
+#endif /* PPP_SERVER */
+
+extern const struct protent pap_protent;
+
+#endif /* UPAP_H */
+#endif /* PPP_SUPPORT && PAP_SUPPORT */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/vj.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/vj.h
new file mode 100644
index 0000000..7f389c8
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/ppp/vj.h
@@ -0,0 +1,161 @@
+/*
+ * 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.
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT && VJ_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#ifndef VJ_H
+#define VJ_H
+
+#include "lwip/ip.h"
+#include "lwip/priv/tcp_priv.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) */
+ u16_t cs_hlen; /* size of hdr (receive only) */
+ u8_t cs_id; /* connection # associated with this state */
+ u8_t 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 {
+ u32_t vjs_packets; /* outbound packets */
+ u32_t vjs_compressed; /* outbound compressed packets */
+ u32_t vjs_searches; /* searches for connection state */
+ u32_t vjs_misses; /* times couldn't find conn. state */
+ u32_t vjs_uncompressedin; /* inbound uncompressed packets */
+ u32_t vjs_compressedin; /* inbound compressed packets */
+ u32_t vjs_errorin; /* inbound unknown type packets */
+ u32_t 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 */
+ u8_t last_recv; /* last rcvd conn. id */
+ u8_t last_xmit; /* last sent conn. id */
+ u16_t flags;
+ u8_t maxSlotIndex;
+ u8_t 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 u8_t 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 */
+
+#endif /* PPP_SUPPORT && VJ_SUPPORT */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/slipif.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/slipif.h
new file mode 100644
index 0000000..65ba31f
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/netif/slipif.h
@@ -0,0 +1,87 @@
+/**
+ * @file
+ *
+ * SLIP netif API
+ */
+
+/*
+ * 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 LWIP_HDR_NETIF_SLIPIF_H
+#define LWIP_HDR_NETIF_SLIPIF_H
+
+#include "lwip/opt.h"
+#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 serial 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 /* LWIP_HDR_NETIF_SLIPIF_H */
+
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/posix/errno.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/posix/errno.h
new file mode 100644
index 0000000..5917c75
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/posix/errno.h
@@ -0,0 +1,33 @@
+/**
+ * @file
+ * This file is a posix wrapper for lwip/errno.h.
+ */
+
+/*
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ */
+
+#include "lwip/errno.h"
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/posix/netdb.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/posix/netdb.h
new file mode 100644
index 0000000..12d4c7f
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/posix/netdb.h
@@ -0,0 +1,33 @@
+/**
+ * @file
+ * This file is a posix wrapper for lwip/netdb.h.
+ */
+
+/*
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ */
+
+#include "lwip/netdb.h"
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/posix/sys/socket.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/posix/sys/socket.h
new file mode 100644
index 0000000..0ed9baf
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/include/posix/sys/socket.h
@@ -0,0 +1,33 @@
+/**
+ * @file
+ * This file is a posix wrapper for lwip/sockets.h.
+ */
+
+/*
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ */
+
+#include "lwip/sockets.h"
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/FILES b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/FILES
new file mode 100644
index 0000000..a3ff431
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/FILES
@@ -0,0 +1,24 @@
+This directory contains generic network interface device drivers that
+do not contain any hardware or architecture specific code. The files
+are:
+
+ethernet.c
+ Shared code for Ethernet based interfaces.
+
+ethernetif.c
+ An example of how an Ethernet device driver could look. This
+ file can be used as a "skeleton" for developing new Ethernet
+ network device drivers. It uses the etharp.c ARP code.
+
+lowpan6.c
+ A 6LoWPAN implementation as a netif.
+
+slipif.c
+ A generic implementation of the SLIP (Serial Line IP)
+ protocol. It requires a sio (serial I/O) module to work.
+
+ppp/ Point-to-Point Protocol stack
+ The lwIP PPP support is based from pppd (http://ppp.samba.org) with
+ huge changes to match code size and memory requirements for embedded
+ devices. Please read /doc/ppp.txt and ppp/PPPD_FOLLOWUP for a detailed
+ explanation.
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/etharp.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/etharp.c
new file mode 100644
index 0000000..4e853ae
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/etharp.c
@@ -0,0 +1,1531 @@
+/**
+ * @file
+ * Address Resolution Protocol module for IP over Ethernet
+ *
+ * Functionally, ARP is divided into two parts. The first maps an IP address
+ * to a physical address when sending a packet, and the second part answers
+ * requests from other machines for our physical address.
+ *
+ * This implementation complies with RFC 826 (Ethernet ARP). It supports
+ * Gratuitious ARP from RFC3220 (IP Mobility Support for IPv4) section 4.6
+ * if an interface calls etharp_gratuitous(our_netif) upon address change.
+ */
+
+/*
+ * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
+ * Copyright (c) 2003-2004 Leon Woestenberg <leon.woestenberg@axon.tv>
+ * Copyright (c) 2003-2004 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_ARP || LWIP_ETHERNET
+
+#include "lwip/ip_addr.h"
+#include "lwip/def.h"
+#include "lwip/ip.h"
+#include "lwip/stats.h"
+#include "lwip/snmp.h"
+#include "lwip/dhcp.h"
+#include "lwip/autoip.h"
+#include "netif/etharp.h"
+#include "lwip/ip6.h"
+
+#if PPPOE_SUPPORT
+#include "netif/ppp/pppoe.h"
+#endif /* PPPOE_SUPPORT */
+
+#include <string.h>
+
+const struct eth_addr ethbroadcast = {{0xff,0xff,0xff,0xff,0xff,0xff}};
+const struct eth_addr ethzero = {{0,0,0,0,0,0}};
+
+/** 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 = 1000, this is
+ * (60 * 5) seconds = 5 minutes.
+ */
+#define ARP_MAXAGE 300
+
+/** 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_UNICAST (ARP_MAXAGE - 30)
+#define ARP_AGE_REREQUEST_USED_BROADCAST (ARP_MAXAGE - 15)
+
+/** the time an ARP entry stays pending after first request,
+ * for ARP_TMR_INTERVAL = 1000, this is
+ * 10 seconds.
+ *
+ * @internal Keep this number at least 2, otherwise it might
+ * run out instantly if the timeout occurs directly after a request.
+ */
+#define ARP_MAXPENDING 5
+
+#define HWTYPE_ETHERNET 1
+
+enum etharp_state {
+ ETHARP_STATE_EMPTY = 0,
+ ETHARP_STATE_PENDING,
+ ETHARP_STATE_STABLE,
+ ETHARP_STATE_STABLE_REREQUESTING_1,
+ ETHARP_STATE_STABLE_REREQUESTING_2
+#if ETHARP_SUPPORT_STATIC_ENTRIES
+ ,ETHARP_STATE_STATIC
+#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
+};
+
+struct etharp_entry {
+#if ARP_QUEUEING
+ /** Pointer to queue of pending outgoing packets on this ARP entry. */
+ struct etharp_q_entry *q;
+#else /* ARP_QUEUEING */
+ /** Pointer to a single pending outgoing packet on this ARP entry. */
+ struct pbuf *q;
+#endif /* ARP_QUEUEING */
+ ip_addr_t ipaddr;
+ struct netif *netif;
+ struct eth_addr ethaddr;
+ u16_t ctime;
+ u8_t state;
+};
+
+static struct etharp_entry arp_table[ARP_TABLE_SIZE];
+
+#if !LWIP_NETIF_HWADDRHINT
+static u8_t etharp_cached_entry;
+#endif /* !LWIP_NETIF_HWADDRHINT */
+
+/** Try hard to create a new entry - we want the IP address to appear in
+ the cache (even if this means removing an active entry or so). */
+#define ETHARP_FLAG_TRY_HARD 1
+#define ETHARP_FLAG_FIND_ONLY 2
+#if ETHARP_SUPPORT_STATIC_ENTRIES
+#define ETHARP_FLAG_STATIC_ENTRY 4
+#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
+
+#if LWIP_NETIF_HWADDRHINT
+#define ETHARP_SET_HINT(netif, hint) if (((netif) != NULL) && ((netif)->addr_hint != NULL)) \
+ *((netif)->addr_hint) = (hint);
+#else /* LWIP_NETIF_HWADDRHINT */
+#define ETHARP_SET_HINT(netif, hint) (etharp_cached_entry = (hint))
+#endif /* LWIP_NETIF_HWADDRHINT */
+
+
+/* Some checks, instead of etharp_init(): */
+#if (LWIP_ARP && (ARP_TABLE_SIZE > 0x7f))
+ #error "ARP_TABLE_SIZE must fit in an s8_t, you have to reduce it in your lwipopts.h"
+#endif
+
+
+static err_t etharp_request_dst(struct netif *netif, const ip_addr_t *ipaddr, const struct eth_addr* hw_dst_addr);
+
+
+#if ARP_QUEUEING
+/**
+ * Free a complete queue of etharp entries
+ *
+ * @param q a qeueue of etharp_q_entry's to free
+ */
+static void
+free_etharp_q(struct etharp_q_entry *q)
+{
+ struct etharp_q_entry *r;
+ LWIP_ASSERT("q != NULL", q != NULL);
+ LWIP_ASSERT("q->p != NULL", q->p != NULL);
+ while (q) {
+ r = q;
+ q = q->next;
+ LWIP_ASSERT("r->p != NULL", (r->p != NULL));
+ pbuf_free(r->p);
+ memp_free(MEMP_ARP_QUEUE, r);
+ }
+}
+#else /* ARP_QUEUEING */
+
+/** Compatibility define: free the queued pbuf */
+#define free_etharp_q(q) pbuf_free(q)
+
+#endif /* ARP_QUEUEING */
+
+/** Clean up ARP table entries */
+static void
+etharp_free_entry(int i)
+{
+ /* remove from SNMP ARP index tree */
+ snmp_delete_arpidx_tree(arp_table[i].netif, &arp_table[i].ipaddr);
+ /* and empty packet queue */
+ if (arp_table[i].q != NULL) {
+ /* remove all queued packets */
+ LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_free_entry: freeing entry %"U16_F", packet queue %p.\n", (u16_t)i, (void *)(arp_table[i].q)));
+ free_etharp_q(arp_table[i].q);
+ arp_table[i].q = NULL;
+ }
+ /* recycle entry for re-use */
+ arp_table[i].state = ETHARP_STATE_EMPTY;
+#ifdef LWIP_DEBUG
+ /* for debugging, clean out the complete entry */
+ arp_table[i].ctime = 0;
+ arp_table[i].netif = NULL;
+ ip_addr_set_zero(&arp_table[i].ipaddr);
+ arp_table[i].ethaddr = ethzero;
+#endif /* LWIP_DEBUG */
+}
+
+/**
+ * Clears expired entries in the ARP table.
+ *
+ * This function should be called every ARP_TMR_INTERVAL milliseconds (1 second),
+ * in order to expire entries in the ARP table.
+ */
+void
+etharp_tmr(void)
+{
+ u8_t i;
+
+ LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_timer\n"));
+ /* remove expired entries from the ARP table */
+ for (i = 0; i < ARP_TABLE_SIZE; ++i) {
+ u8_t state = arp_table[i].state;
+ if (state != ETHARP_STATE_EMPTY
+#if ETHARP_SUPPORT_STATIC_ENTRIES
+ && (state != ETHARP_STATE_STATIC)
+#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
+ ) {
+ arp_table[i].ctime++;
+ if ((arp_table[i].ctime >= ARP_MAXAGE) ||
+ ((arp_table[i].state == ETHARP_STATE_PENDING) &&
+ (arp_table[i].ctime >= ARP_MAXPENDING))) {
+ /* pending or stable entry has become old! */
+ LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_timer: expired %s entry %"U16_F".\n",
+ arp_table[i].state >= ETHARP_STATE_STABLE ? "stable" : "pending", (u16_t)i));
+ /* clean up entries that have just been expired */
+ etharp_free_entry(i);
+ } else if (arp_table[i].state == ETHARP_STATE_STABLE_REREQUESTING_1) {
+ /* Don't send more than one request every 2 seconds. */
+ arp_table[i].state = ETHARP_STATE_STABLE_REREQUESTING_2;
+ } else if (arp_table[i].state == ETHARP_STATE_STABLE_REREQUESTING_2) {
+ /* Reset state to stable, so that the next transmitted packet will
+ re-send an ARP request. */
+ arp_table[i].state = ETHARP_STATE_STABLE;
+ }
+ /* still pending entry? (not expired) */
+ else if (arp_table[i].state == ETHARP_STATE_PENDING) {
+ /* resend an ARP query here? */
+ if (etharp_request(arp_table[i].netif, &arp_table[i].ipaddr) != ERR_OK) {
+ }
+ }
+ }
+ }
+}
+
+/**
+ * Search the ARP table for a matching or new entry.
+ *
+ * If an IP address is given, return a pending or stable ARP entry that matches
+ * the address. If no match is found, create a new entry with this address set,
+ * but in state ETHARP_EMPTY. The caller must check and possibly change the
+ * state of the returned entry.
+ *
+ * If ipaddr is NULL, return a initialized new entry in state ETHARP_EMPTY.
+ *
+ * In all cases, attempt to create new entries from an empty entry. If no
+ * empty entries are available and ETHARP_FLAG_TRY_HARD flag is set, recycle
+ * old entries. Heuristic choose the least important entry for recycling.
+ *
+ * @param ipaddr IP address to find in ARP cache, or to add if not found.
+ * @param flags @see definition of ETHARP_FLAG_*
+ * @param netif netif related to this address (used for NETIF_HWADDRHINT)
+ *
+ * @return The ARP entry index that matched or is created, ERR_MEM if no
+ * entry is found or could be recycled.
+ */
+static s8_t
+etharp_find_entry(const ip_addr_t *ipaddr, u8_t flags, struct netif* netif)
+{
+ s8_t old_pending = ARP_TABLE_SIZE, old_stable = ARP_TABLE_SIZE;
+ s8_t empty = ARP_TABLE_SIZE;
+ u8_t i = 0;
+ /* oldest entry with packets on queue */
+ s8_t old_queue = ARP_TABLE_SIZE;
+ /* its age */
+ u16_t age_queue = 0, age_pending = 0, age_stable = 0;
+
+ LWIP_UNUSED_ARG(netif);
+
+ /**
+ * a) do a search through the cache, remember candidates
+ * b) select candidate entry
+ * c) create new entry
+ */
+
+ /* a) in a single search sweep, do all of this
+ * 1) remember the first empty entry (if any)
+ * 2) remember the oldest stable entry (if any)
+ * 3) remember the oldest pending entry without queued packets (if any)
+ * 4) remember the oldest pending entry with queued packets (if any)
+ * 5) search for a matching IP entry, either pending or stable
+ * until 5 matches, or all entries are searched for.
+ */
+
+ for (i = 0; i < ARP_TABLE_SIZE; ++i) {
+ u8_t state = arp_table[i].state;
+ /* no empty entry found yet and now we do find one? */
+ if ((empty == ARP_TABLE_SIZE) && (state == ETHARP_STATE_EMPTY)) {
+ LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_find_entry: found empty entry %"U16_F"\n", (u16_t)i));
+ /* remember first empty entry */
+ empty = i;
+ } else if (state != ETHARP_STATE_EMPTY) {
+ LWIP_ASSERT("state == ETHARP_STATE_PENDING || state >= ETHARP_STATE_STABLE",
+ state == ETHARP_STATE_PENDING || state >= ETHARP_STATE_STABLE);
+ /* if given, does IP address match IP address in ARP entry? */
+ if (ipaddr && ip_addr_cmp(ipaddr, &arp_table[i].ipaddr)
+#if ETHARP_TABLE_MATCH_NETIF
+ && ((netif == NULL) || (netif == arp_table[i].netif))
+#endif /* ETHARP_TABLE_MATCH_NETIF */
+ ) {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: found matching entry %"U16_F"\n", (u16_t)i));
+ /* found exact IP address match, simply bail out */
+ return i;
+ }
+ /* pending entry? */
+ if (state == ETHARP_STATE_PENDING) {
+ /* pending with queued packets? */
+ if (arp_table[i].q != NULL) {
+ if (arp_table[i].ctime >= age_queue) {
+ old_queue = i;
+ age_queue = arp_table[i].ctime;
+ }
+ } else
+ /* pending without queued packets? */
+ {
+ if (arp_table[i].ctime >= age_pending) {
+ old_pending = i;
+ age_pending = arp_table[i].ctime;
+ }
+ }
+ /* stable entry? */
+ } else if (state >= ETHARP_STATE_STABLE) {
+#if ETHARP_SUPPORT_STATIC_ENTRIES
+ /* don't record old_stable for static entries since they never expire */
+ if (state < ETHARP_STATE_STATIC)
+#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
+ {
+ /* remember entry with oldest stable entry in oldest, its age in maxtime */
+ if (arp_table[i].ctime >= age_stable) {
+ old_stable = i;
+ age_stable = arp_table[i].ctime;
+ }
+ }
+ }
+ }
+ }
+ /* { we have no match } => try to create a new entry */
+
+ /* don't create new entry, only search? */
+ if (((flags & ETHARP_FLAG_FIND_ONLY) != 0) ||
+ /* or no empty entry found and not allowed to recycle? */
+ ((empty == ARP_TABLE_SIZE) && ((flags & ETHARP_FLAG_TRY_HARD) == 0))) {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: no empty entry found and not allowed to recycle\n"));
+ return (s8_t)ERR_MEM;
+ }
+
+ /* b) choose the least destructive entry to recycle:
+ * 1) empty entry
+ * 2) oldest stable entry
+ * 3) oldest pending entry without queued packets
+ * 4) oldest pending entry with queued packets
+ *
+ * { ETHARP_FLAG_TRY_HARD is set at this point }
+ */
+
+ /* 1) empty entry available? */
+ if (empty < ARP_TABLE_SIZE) {
+ i = empty;
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: selecting empty entry %"U16_F"\n", (u16_t)i));
+ } else {
+ /* 2) found recyclable stable entry? */
+ if (old_stable < ARP_TABLE_SIZE) {
+ /* recycle oldest stable*/
+ i = old_stable;
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: selecting oldest stable entry %"U16_F"\n", (u16_t)i));
+ /* no queued packets should exist on stable entries */
+ LWIP_ASSERT("arp_table[i].q == NULL", arp_table[i].q == NULL);
+ /* 3) found recyclable pending entry without queued packets? */
+ } else if (old_pending < ARP_TABLE_SIZE) {
+ /* recycle oldest pending */
+ i = old_pending;
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: selecting oldest pending entry %"U16_F" (without queue)\n", (u16_t)i));
+ /* 4) found recyclable pending entry with queued packets? */
+ } else if (old_queue < ARP_TABLE_SIZE) {
+ /* recycle oldest pending (queued packets are free in etharp_free_entry) */
+ i = old_queue;
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: selecting oldest pending entry %"U16_F", freeing packet queue %p\n", (u16_t)i, (void *)(arp_table[i].q)));
+ /* no empty or recyclable entries found */
+ } else {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: no empty or recyclable entries found\n"));
+ return (s8_t)ERR_MEM;
+ }
+
+ /* { empty or recyclable entry found } */
+ LWIP_ASSERT("i < ARP_TABLE_SIZE", i < ARP_TABLE_SIZE);
+ etharp_free_entry(i);
+ }
+
+ LWIP_ASSERT("i < ARP_TABLE_SIZE", i < ARP_TABLE_SIZE);
+ LWIP_ASSERT("arp_table[i].state == ETHARP_STATE_EMPTY",
+ arp_table[i].state == ETHARP_STATE_EMPTY);
+
+ /* IP address given? */
+ if (ipaddr != NULL) {
+ /* set IP address */
+ ip_addr_copy(arp_table[i].ipaddr, *ipaddr);
+ }
+ arp_table[i].ctime = 0;
+#if ETHARP_TABLE_MATCH_NETIF
+ arp_table[i].netif = netif;
+#endif /* ETHARP_TABLE_MATCH_NETIF*/
+ return (err_t)i;
+}
+
+/**
+ * Send an IP packet on the network using netif->linkoutput
+ * The ethernet header is filled in before sending.
+ *
+ * @params netif the lwIP network interface on which to send the packet
+ * @params p the packet to send, p->payload pointing to the (uninitialized) ethernet header
+ * @params src the source MAC address to be copied into the ethernet header
+ * @params dst the destination MAC address to be copied into the ethernet header
+ * @return ERR_OK if the packet was sent, any other err_t on failure
+ */
+static err_t
+etharp_send_ip(struct netif *netif, struct pbuf *p, struct eth_addr *src, struct eth_addr *dst)
+{
+ struct eth_hdr *ethhdr = (struct eth_hdr *)p->payload;
+#if ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET)
+ struct eth_vlan_hdr *vlanhdr;
+#endif /* ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET) */
+
+ LWIP_ASSERT("netif->hwaddr_len must be the same as ETHARP_HWADDR_LEN for etharp!",
+ (netif->hwaddr_len == ETHARP_HWADDR_LEN));
+#if ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET)
+ ethhdr->type = PP_HTONS(ETHTYPE_VLAN);
+ vlanhdr = (struct eth_vlan_hdr*)(((u8_t*)ethhdr) + SIZEOF_ETH_HDR);
+ vlanhdr->prio_vid = 0;
+ vlanhdr->tpid = PP_HTONS(ETHTYPE_IP);
+ if (!LWIP_HOOK_VLAN_SET(netif, ethhdr, vlanhdr)) {
+ /* packet shall not contain VLAN header, so hide it and set correct ethertype */
+ pbuf_header(p, -SIZEOF_VLAN_HDR);
+ ethhdr = (struct eth_hdr *)p->payload;
+#endif /* ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET) */
+ ethhdr->type = PP_HTONS(ETHTYPE_IP);
+#if ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET)
+ }
+#endif /* ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET) */
+ ETHADDR32_COPY(&ethhdr->dest, dst);
+ ETHADDR16_COPY(&ethhdr->src, src);
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_send_ip: sending packet %p\n", (void *)p));
+ /* send the packet */
+ return netif->linkoutput(netif, p);
+}
+
+/**
+ * Update (or insert) a IP/MAC address pair in the ARP cache.
+ *
+ * If a pending entry is resolved, any queued packets will be sent
+ * at this point.
+ *
+ * @param netif netif related to this entry (used for NETIF_ADDRHINT)
+ * @param ipaddr IP address of the inserted ARP entry.
+ * @param ethaddr Ethernet address of the inserted ARP entry.
+ * @param flags @see definition of ETHARP_FLAG_*
+ *
+ * @return
+ * - ERR_OK Successfully updated ARP cache.
+ * - ERR_MEM If we could not add a new ARP entry when ETHARP_FLAG_TRY_HARD was set.
+ * - ERR_ARG Non-unicast address given, those will not appear in ARP cache.
+ *
+ * @see pbuf_free()
+ */
+static err_t
+etharp_update_arp_entry(struct netif *netif, const ip_addr_t *ipaddr, struct eth_addr *ethaddr, u8_t flags)
+{
+ s8_t i;
+ LWIP_ASSERT("netif->hwaddr_len == ETHARP_HWADDR_LEN", netif->hwaddr_len == ETHARP_HWADDR_LEN);
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_update_arp_entry: %"U16_F".%"U16_F".%"U16_F".%"U16_F" - %02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F"\n",
+ ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr), ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr),
+ ethaddr->addr[0], ethaddr->addr[1], ethaddr->addr[2],
+ ethaddr->addr[3], ethaddr->addr[4], ethaddr->addr[5]));
+ /* non-unicast address? */
+ if (ip_addr_isany(ipaddr) ||
+ ip_addr_isbroadcast(ipaddr, netif) ||
+ ip_addr_ismulticast(ipaddr)) {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_update_arp_entry: will not add non-unicast IP address to ARP cache\n"));
+ return ERR_ARG;
+ }
+ /* find or create ARP entry */
+ i = etharp_find_entry(ipaddr, flags, netif);
+ /* bail out if no entry could be found */
+ if (i < 0) {
+ return (err_t)i;
+ }
+
+#if ETHARP_SUPPORT_STATIC_ENTRIES
+ if (flags & ETHARP_FLAG_STATIC_ENTRY) {
+ /* record static type */
+ arp_table[i].state = ETHARP_STATE_STATIC;
+ } else
+#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
+ {
+ /* mark it stable */
+ arp_table[i].state = ETHARP_STATE_STABLE;
+ }
+
+ /* record network interface */
+ arp_table[i].netif = netif;
+ /* insert in SNMP ARP index tree */
+ snmp_insert_arpidx_tree(netif, &arp_table[i].ipaddr);
+
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_update_arp_entry: updating stable entry %"S16_F"\n", (s16_t)i));
+ /* update address */
+ ETHADDR32_COPY(&arp_table[i].ethaddr, ethaddr);
+ /* reset time stamp */
+ arp_table[i].ctime = 0;
+ /* this is where we will send out queued packets! */
+#if ARP_QUEUEING
+ while (arp_table[i].q != NULL) {
+ struct pbuf *p;
+ /* remember remainder of queue */
+ struct etharp_q_entry *q = arp_table[i].q;
+ /* pop first item off the queue */
+ arp_table[i].q = q->next;
+ /* get the packet pointer */
+ p = q->p;
+ /* now queue entry can be freed */
+ memp_free(MEMP_ARP_QUEUE, q);
+#else /* ARP_QUEUEING */
+ if (arp_table[i].q != NULL) {
+ struct pbuf *p = arp_table[i].q;
+ arp_table[i].q = NULL;
+#endif /* ARP_QUEUEING */
+ /* send the queued IP packet */
+ etharp_send_ip(netif, p, (struct eth_addr*)(netif->hwaddr), ethaddr);
+ /* free the queued IP packet */
+ pbuf_free(p);
+ }
+ return ERR_OK;
+}
+
+#if ETHARP_SUPPORT_STATIC_ENTRIES
+/** Add a new static entry to the ARP table. If an entry exists for the
+ * specified IP address, this entry is overwritten.
+ * If packets are queued for the specified IP address, they are sent out.
+ *
+ * @param ipaddr IP address for the new static entry
+ * @param ethaddr ethernet address for the new static entry
+ * @return @see return values of etharp_add_static_entry
+ */
+err_t
+etharp_add_static_entry(const ip_addr_t *ipaddr, struct eth_addr *ethaddr)
+{
+ struct netif *netif;
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_add_static_entry: %"U16_F".%"U16_F".%"U16_F".%"U16_F" - %02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F"\n",
+ ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr), ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr),
+ ethaddr->addr[0], ethaddr->addr[1], ethaddr->addr[2],
+ ethaddr->addr[3], ethaddr->addr[4], ethaddr->addr[5]));
+
+ netif = ip_route(ipaddr);
+ if (netif == NULL) {
+ return ERR_RTE;
+ }
+
+ return etharp_update_arp_entry(netif, ipaddr, ethaddr, ETHARP_FLAG_TRY_HARD | ETHARP_FLAG_STATIC_ENTRY);
+}
+
+/** Remove a static entry from the ARP table previously added with a call to
+ * etharp_add_static_entry.
+ *
+ * @param ipaddr IP address of the static entry to remove
+ * @return ERR_OK: entry removed
+ * ERR_MEM: entry wasn't found
+ * ERR_ARG: entry wasn't a static entry but a dynamic one
+ */
+err_t
+etharp_remove_static_entry(const ip_addr_t *ipaddr)
+{
+ s8_t i;
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_remove_static_entry: %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr), ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr)));
+
+ /* find or create ARP entry */
+ i = etharp_find_entry(ipaddr, ETHARP_FLAG_FIND_ONLY, NULL);
+ /* bail out if no entry could be found */
+ if (i < 0) {
+ return (err_t)i;
+ }
+
+ if (arp_table[i].state != ETHARP_STATE_STATIC) {
+ /* entry wasn't a static entry, cannot remove it */
+ return ERR_ARG;
+ }
+ /* entry found, free it */
+ etharp_free_entry(i);
+ return ERR_OK;
+}
+#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
+
+/**
+ * Remove all ARP table entries of the specified netif.
+ *
+ * @param netif points to a network interface
+ */
+void etharp_cleanup_netif(struct netif *netif)
+{
+ u8_t i;
+
+ for (i = 0; i < ARP_TABLE_SIZE; ++i) {
+ u8_t state = arp_table[i].state;
+ if ((state != ETHARP_STATE_EMPTY) && (arp_table[i].netif == netif)) {
+ etharp_free_entry(i);
+ }
+ }
+}
+
+/**
+ * Finds (stable) ethernet/IP address pair from ARP table
+ * using interface and IP address index.
+ * @note the addresses in the ARP table are in network order!
+ *
+ * @param netif points to interface index
+ * @param ipaddr points to the (network order) IP address index
+ * @param eth_ret points to return pointer
+ * @param ip_ret points to return pointer
+ * @return table index if found, -1 otherwise
+ */
+s8_t
+etharp_find_addr(struct netif *netif, const ip_addr_t *ipaddr,
+ struct eth_addr **eth_ret, const ip_addr_t **ip_ret)
+{
+ s8_t i;
+
+ LWIP_ASSERT("eth_ret != NULL && ip_ret != NULL",
+ eth_ret != NULL && ip_ret != NULL);
+
+ LWIP_UNUSED_ARG(netif);
+
+ i = etharp_find_entry(ipaddr, ETHARP_FLAG_FIND_ONLY, netif);
+ if((i >= 0) && (arp_table[i].state >= ETHARP_STATE_STABLE)) {
+ *eth_ret = &arp_table[i].ethaddr;
+ *ip_ret = &arp_table[i].ipaddr;
+ return i;
+ }
+ return -1;
+}
+
+#if ETHARP_TRUST_IP_MAC
+/**
+ * Updates the ARP table using the given IP packet.
+ *
+ * Uses the incoming IP packet's source address to update the
+ * ARP cache for the local network. The function does not alter
+ * or free the packet. This function must be called before the
+ * packet p is passed to the IP layer.
+ *
+ * @param netif The lwIP network interface on which the IP packet pbuf arrived.
+ * @param p The IP packet that arrived on netif.
+ *
+ * @return NULL
+ *
+ * @see pbuf_free()
+ */
+static void
+etharp_ip_input(struct netif *netif, struct pbuf *p)
+{
+ struct eth_hdr *ethhdr;
+ struct ip_hdr *iphdr;
+ ip_addr_t iphdr_src;
+ LWIP_ERROR("netif != NULL", (netif != NULL), return;);
+
+ /* Only insert an entry if the source IP address of the
+ incoming IP packet comes from a host on the local network. */
+ ethhdr = (struct eth_hdr *)p->payload;
+ iphdr = (struct ip_hdr *)((u8_t*)ethhdr + SIZEOF_ETH_HDR);
+#if ETHARP_SUPPORT_VLAN
+ if (ethhdr->type == PP_HTONS(ETHTYPE_VLAN)) {
+ iphdr = (struct ip_hdr *)((u8_t*)ethhdr + SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR);
+ }
+#endif /* ETHARP_SUPPORT_VLAN */
+
+ ip_addr_copy(iphdr_src, iphdr->src);
+
+ /* source is not on the local network? */
+ if (!ip_addr_netcmp(&iphdr_src, &(netif->ip_addr), &(netif->netmask))) {
+ /* do nothing */
+ return;
+ }
+
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_ip_input: updating ETHARP table.\n"));
+ /* update the source IP address in the cache, if present */
+ /* @todo We could use ETHARP_FLAG_TRY_HARD if we think we are going to talk
+ * back soon (for example, if the destination IP address is ours. */
+ etharp_update_arp_entry(netif, &iphdr_src, &(ethhdr->src), ETHARP_FLAG_FIND_ONLY);
+}
+#endif /* ETHARP_TRUST_IP_MAC */
+
+/**
+ * Responds to ARP requests to us. Upon ARP replies to us, add entry to cache
+ * send out queued IP packets. Updates cache with snooped address pairs.
+ *
+ * Should be called for incoming ARP packets. The pbuf in the argument
+ * is freed by this function.
+ *
+ * @param netif The lwIP network interface on which the ARP packet pbuf arrived.
+ * @param ethaddr Ethernet address of netif.
+ * @param p The ARP packet that arrived on netif. Is freed by this function.
+ *
+ * @return NULL
+ *
+ * @see pbuf_free()
+ */
+static void
+etharp_arp_input(struct netif *netif, struct eth_addr *ethaddr, struct pbuf *p)
+{
+ struct etharp_hdr *hdr;
+ struct eth_hdr *ethhdr;
+ /* these are aligned properly, whereas the ARP header fields might not be */
+ ip_addr_t sipaddr, dipaddr;
+ u8_t for_us;
+#if LWIP_AUTOIP
+ const u8_t * ethdst_hwaddr;
+#endif /* LWIP_AUTOIP */
+
+ LWIP_ERROR("netif != NULL", (netif != NULL), return;);
+
+ /* drop short ARP packets: we have to check for p->len instead of p->tot_len here
+ since a struct etharp_hdr is pointed to p->payload, so it musn't be chained! */
+ if (p->len < SIZEOF_ETHARP_PACKET) {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING,
+ ("etharp_arp_input: packet dropped, too short (%"S16_F"/%"S16_F")\n", p->tot_len,
+ (s16_t)SIZEOF_ETHARP_PACKET));
+ ETHARP_STATS_INC(etharp.lenerr);
+ ETHARP_STATS_INC(etharp.drop);
+ pbuf_free(p);
+ return;
+ }
+
+ ethhdr = (struct eth_hdr *)p->payload;
+ hdr = (struct etharp_hdr *)((u8_t*)ethhdr + SIZEOF_ETH_HDR);
+#if ETHARP_SUPPORT_VLAN
+ if (ethhdr->type == PP_HTONS(ETHTYPE_VLAN)) {
+ hdr = (struct etharp_hdr *)(((u8_t*)ethhdr) + SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR);
+ }
+#endif /* ETHARP_SUPPORT_VLAN */
+
+ /* RFC 826 "Packet Reception": */
+ if ((hdr->hwtype != PP_HTONS(HWTYPE_ETHERNET)) ||
+ (hdr->hwlen != ETHARP_HWADDR_LEN) ||
+ (hdr->protolen != sizeof(ip_addr_t)) ||
+ (hdr->proto != PP_HTONS(ETHTYPE_IP))) {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING,
+ ("etharp_arp_input: packet dropped, wrong hw type, hwlen, proto, protolen or ethernet type (%"U16_F"/%"U16_F"/%"U16_F"/%"U16_F")\n",
+ hdr->hwtype, hdr->hwlen, hdr->proto, hdr->protolen));
+ ETHARP_STATS_INC(etharp.proterr);
+ ETHARP_STATS_INC(etharp.drop);
+ pbuf_free(p);
+ return;
+ }
+ ETHARP_STATS_INC(etharp.recv);
+
+#if LWIP_AUTOIP
+ /* We have to check if a host already has configured our random
+ * created link local address and continuously check if there is
+ * a host with this IP-address so we can detect collisions */
+ autoip_arp_reply(netif, hdr);
+#endif /* LWIP_AUTOIP */
+
+ /* Copy struct ip_addr2 to aligned ip_addr, to support compilers without
+ * structure packing (not using structure copy which breaks strict-aliasing rules). */
+ IPADDR2_COPY(&sipaddr, &hdr->sipaddr);
+ IPADDR2_COPY(&dipaddr, &hdr->dipaddr);
+
+ /* this interface is not configured? */
+ if (ip_addr_isany(&netif->ip_addr)) {
+ for_us = 0;
+ } else {
+ /* ARP packet directed to us? */
+ for_us = (u8_t)ip_addr_cmp(&dipaddr, &(netif->ip_addr));
+ }
+
+ /* ARP message directed to us?
+ -> add IP address in ARP cache; assume requester wants to talk to us,
+ can result in directly sending the queued packets for this host.
+ ARP message not directed to us?
+ -> update the source IP address in the cache, if present */
+ etharp_update_arp_entry(netif, &sipaddr, &(hdr->shwaddr),
+ for_us ? ETHARP_FLAG_TRY_HARD : ETHARP_FLAG_FIND_ONLY);
+
+ /* now act on the message itself */
+ switch (hdr->opcode) {
+ /* ARP request? */
+ case PP_HTONS(ARP_REQUEST):
+ /* ARP request. If it asked for our address, we send out a
+ * reply. In any case, we time-stamp any existing ARP entry,
+ * and possibly send out an IP packet that was queued on it. */
+
+ LWIP_DEBUGF (ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: incoming ARP request\n"));
+ /* ARP request for our address? */
+ if (for_us) {
+
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: replying to ARP request for our IP address\n"));
+ /* Re-use pbuf to send ARP reply.
+ Since we are re-using an existing pbuf, we can't call etharp_raw since
+ that would allocate a new pbuf. */
+ hdr->opcode = htons(ARP_REPLY);
+
+ IPADDR2_COPY(&hdr->dipaddr, &hdr->sipaddr);
+ IPADDR2_COPY(&hdr->sipaddr, &netif->ip_addr);
+
+ LWIP_ASSERT("netif->hwaddr_len must be the same as ETHARP_HWADDR_LEN for etharp!",
+ (netif->hwaddr_len == ETHARP_HWADDR_LEN));
+#if LWIP_AUTOIP
+ /* If we are using Link-Local, all ARP packets that contain a Link-Local
+ * 'sender IP address' MUST be sent using link-layer broadcast instead of
+ * link-layer unicast. (See RFC3927 Section 2.5, last paragraph) */
+ ethdst_hwaddr = ip_addr_islinklocal(&netif->ip_addr) ? (u8_t*)(ethbroadcast.addr) : hdr->shwaddr.addr;
+#endif /* LWIP_AUTOIP */
+
+ ETHADDR16_COPY(&hdr->dhwaddr, &hdr->shwaddr);
+#if LWIP_AUTOIP
+ ETHADDR16_COPY(&ethhdr->dest, ethdst_hwaddr);
+#else /* LWIP_AUTOIP */
+ ETHADDR16_COPY(&ethhdr->dest, &hdr->shwaddr);
+#endif /* LWIP_AUTOIP */
+ ETHADDR16_COPY(&hdr->shwaddr, ethaddr);
+ ETHADDR16_COPY(&ethhdr->src, ethaddr);
+
+ /* hwtype, hwaddr_len, proto, protolen and the type in the ethernet header
+ are already correct, we tested that before */
+
+ /* return ARP reply */
+ netif->linkoutput(netif, p);
+ /* we are not configured? */
+ } else if (ip_addr_isany(&netif->ip_addr)) {
+ /* { for_us == 0 and netif->ip_addr.addr == 0 } */
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: we are unconfigured, ARP request ignored.\n"));
+ /* request was not directed to us */
+ } else {
+ /* { for_us == 0 and netif->ip_addr.addr != 0 } */
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: ARP request was not for us.\n"));
+ }
+ break;
+ case PP_HTONS(ARP_REPLY):
+ /* ARP reply. We already updated the ARP cache earlier. */
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: incoming ARP reply\n"));
+#if (LWIP_DHCP && DHCP_DOES_ARP_CHECK)
+ /* DHCP wants to know about ARP replies from any host with an
+ * IP address also offered to us by the DHCP server. We do not
+ * want to take a duplicate IP address on a single network.
+ * @todo How should we handle redundant (fail-over) interfaces? */
+ dhcp_arp_reply(netif, &sipaddr);
+#endif /* (LWIP_DHCP && DHCP_DOES_ARP_CHECK) */
+ break;
+ default:
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: ARP unknown opcode type %"S16_F"\n", htons(hdr->opcode)));
+ ETHARP_STATS_INC(etharp.err);
+ break;
+ }
+ /* free ARP packet */
+ pbuf_free(p);
+}
+
+/** 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) {
+ if (arp_table[arp_idx].ctime >= ARP_AGE_REREQUEST_USED_BROADCAST) {
+ /* issue a standard request using broadcast */
+ if (etharp_request(netif, &arp_table[arp_idx].ipaddr) == ERR_OK) {
+ arp_table[arp_idx].state = ETHARP_STATE_STABLE_REREQUESTING_1;
+ }
+ } else if (arp_table[arp_idx].ctime >= ARP_AGE_REREQUEST_USED_UNICAST) {
+ /* issue a unicast request (for 15 seconds) to prevent unnecessary broadcast */
+ if (etharp_request_dst(netif, &arp_table[arp_idx].ipaddr, &arp_table[arp_idx].ethaddr) == ERR_OK) {
+ arp_table[arp_idx].state = ETHARP_STATE_STABLE_REREQUESTING_1;
+ }
+ }
+ }
+
+ return etharp_send_ip(netif, q, (struct eth_addr*)(netif->hwaddr),
+ &arp_table[arp_idx].ethaddr);
+}
+
+/**
+ * Resolve and fill-in Ethernet address header for outgoing IP packet.
+ *
+ * For IP multicast and broadcast, corresponding Ethernet addresses
+ * are selected and the packet is transmitted on the link.
+ *
+ * For unicast addresses, the packet is submitted to etharp_query(). In
+ * case the IP address is outside the local network, the IP address of
+ * the gateway is used.
+ *
+ * @param netif The lwIP network interface which the IP packet will be sent on.
+ * @param q The pbuf(s) containing the IP packet to be sent.
+ * @param ipaddr The IP address of the packet destination.
+ *
+ * @return
+ * - ERR_RTE No route to destination (no gateway to external networks),
+ * or the return type of either etharp_query() or etharp_send_ip().
+ */
+err_t
+etharp_output(struct netif *netif, struct pbuf *q, const ip_addr_t *ipaddr)
+{
+ struct eth_addr *dest;
+ struct eth_addr mcastaddr;
+ const 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 ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET)
+ if (pbuf_header(q, sizeof(struct eth_hdr) + SIZEOF_VLAN_HDR) != 0) {
+#else /* ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET) */
+ if (pbuf_header(q, sizeof(struct eth_hdr)) != 0) {
+#endif /* ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET) */
+ /* bail out */
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
+ ("etharp_output: could not allocate room for header.\n"));
+ LINK_STATS_INC(link.lenerr);
+ return ERR_BUF;
+ }
+
+ /* Determine on destination hardware address. Broadcasts and multicasts
+ * are special, other IP addresses are looked up in the ARP table. */
+
+ /* broadcast destination IP address? */
+ if (ip_addr_isbroadcast(ipaddr, netif)) {
+ /* broadcast on Ethernet also */
+ dest = (struct eth_addr *)&ethbroadcast;
+ /* multicast destination IP address? */
+ } else if (ip_addr_ismulticast(ipaddr)) {
+ /* Hash IP multicast address to MAC address.*/
+ mcastaddr.addr[0] = LL_MULTICAST_ADDR_0;
+ mcastaddr.addr[1] = LL_MULTICAST_ADDR_1;
+ mcastaddr.addr[2] = LL_MULTICAST_ADDR_2;
+ mcastaddr.addr[3] = ip4_addr2(ipaddr) & 0x7f;
+ mcastaddr.addr[4] = ip4_addr3(ipaddr);
+ mcastaddr.addr[5] = ip4_addr4(ipaddr);
+ /* destination Ethernet address is multicast */
+ dest = &mcastaddr;
+ /* unicast destination IP address? */
+ } else {
+ s8_t i;
+ /* outside local network? if so, this can neither be a global broadcast nor
+ a subnet broadcast. */
+ if (!ip_addr_netcmp(ipaddr, &(netif->ip_addr), &(netif->netmask)) &&
+ !ip_addr_islinklocal(ipaddr)) {
+#if LWIP_AUTOIP
+ struct ip_hdr *iphdr = (struct ip_hdr*)((u8_t*)q->payload +
+#if ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET)
+ SIZEOF_VLAN_HDR +
+#endif /* ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET) */
+ sizeof(struct eth_hdr));
+ /* According to RFC 3297, chapter 2.6.2 (Forwarding Rules), a packet with
+ a link-local source address must always be "directly to its destination
+ on the same physical link. The host MUST NOT send the packet to any
+ router for forwarding". */
+ if (!ip_addr_islinklocal(&iphdr->src))
+#endif /* LWIP_AUTOIP */
+ {
+#ifdef LWIP_HOOK_ETHARP_GET_GW
+ /* For advanced routing, a single default gateway might not be enough, so get
+ the IP address of the gateway to handle the current destination address. */
+ dst_addr = LWIP_HOOK_ETHARP_GET_GW(netif, ipaddr);
+ if(dst_addr == NULL)
+#endif /* LWIP_HOOK_ETHARP_GET_GW */
+ {
+ /* interface has default gateway? */
+ if (!ip_addr_isany(&netif->gw)) {
+ /* send to hardware address of default gateway IP address */
+ dst_addr = &(netif->gw);
+ /* no default gateway available */
+ } else {
+ /* no route to destination error (default gateway missing) */
+ return ERR_RTE;
+ }
+ }
+ }
+ }
+#if LWIP_NETIF_HWADDRHINT
+ if (netif->addr_hint != NULL) {
+ /* per-pcb cached entry was given */
+ u8_t etharp_cached_entry = *(netif->addr_hint);
+ if (etharp_cached_entry < ARP_TABLE_SIZE) {
+#endif /* LWIP_NETIF_HWADDRHINT */
+ if ((arp_table[etharp_cached_entry].state >= ETHARP_STATE_STABLE) &&
+ (ip_addr_cmp(dst_addr, &arp_table[etharp_cached_entry].ipaddr))) {
+ /* the per-pcb-cached entry is stable and the right one! */
+ ETHARP_STATS_INC(etharp.cachehit);
+ return etharp_output_to_arp_index(netif, q, etharp_cached_entry);
+ }
+#if LWIP_NETIF_HWADDRHINT
+ }
+ }
+#endif /* LWIP_NETIF_HWADDRHINT */
+
+ /* find stable entry: do this here since this is a critical path for
+ throughput and etharp_find_entry() is kind of slow */
+ for (i = 0; i < ARP_TABLE_SIZE; i++) {
+ if ((arp_table[i].state >= ETHARP_STATE_STABLE) &&
+ (ip_addr_cmp(dst_addr, &arp_table[i].ipaddr))) {
+ /* found an existing, stable entry */
+ ETHARP_SET_HINT(netif, i);
+ return etharp_output_to_arp_index(netif, q, i);
+ }
+ }
+ /* no stable entry found, use the (slower) query function:
+ queue on destination Ethernet address belonging to ipaddr */
+ return etharp_query(netif, dst_addr, q);
+ }
+
+ /* continuation for multicast/broadcast destinations */
+ /* obtain source Ethernet address of the given interface */
+ /* send packet directly on the link */
+ return etharp_send_ip(netif, q, (struct eth_addr*)(netif->hwaddr), dest);
+}
+
+/**
+ * Send an ARP request for the given IP address and/or queue a packet.
+ *
+ * If the IP address was not yet in the cache, a pending ARP cache entry
+ * is added and an ARP request is sent for the given address. The packet
+ * is queued on this entry.
+ *
+ * If the IP address was already pending in the cache, a new ARP request
+ * is sent for the given address. The packet is queued on this entry.
+ *
+ * If the IP address was already stable in the cache, and a packet is
+ * given, it is directly sent and no ARP request is sent out.
+ *
+ * If the IP address was already stable in the cache, and no packet is
+ * given, an ARP request is sent out.
+ *
+ * @param netif The lwIP network interface on which ipaddr
+ * must be queried for.
+ * @param ipaddr The IP address to be resolved.
+ * @param q If non-NULL, a pbuf that must be delivered to the IP address.
+ * q is not freed by this function.
+ *
+ * @note q must only be ONE packet, not a packet queue!
+ *
+ * @return
+ * - ERR_BUF Could not make room for Ethernet header.
+ * - ERR_MEM Hardware address unknown, and no more ARP entries available
+ * to query for address or queue the packet.
+ * - ERR_MEM Could not queue packet due to memory shortage.
+ * - ERR_RTE No route to destination (no gateway to external networks).
+ * - ERR_ARG Non-unicast address given, those will not appear in ARP cache.
+ *
+ */
+err_t
+etharp_query(struct netif *netif, const ip_addr_t *ipaddr, struct pbuf *q)
+{
+ struct eth_addr * srcaddr = (struct eth_addr *)netif->hwaddr;
+ err_t result = ERR_MEM;
+ int is_new_entry = 0;
+ s8_t i; /* ARP entry index */
+
+ /* non-unicast address? */
+ if (ip_addr_isbroadcast(ipaddr, netif) ||
+ ip_addr_ismulticast(ipaddr) ||
+ ip_addr_isany(ipaddr)) {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: will not add non-unicast IP address to ARP cache\n"));
+ return ERR_ARG;
+ }
+
+ /* find entry in ARP cache, ask to create entry if queueing packet */
+ i = etharp_find_entry(ipaddr, ETHARP_FLAG_TRY_HARD, netif);
+
+ /* could not find or create entry? */
+ if (i < 0) {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: could not create ARP entry\n"));
+ if (q) {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: packet dropped\n"));
+ ETHARP_STATS_INC(etharp.memerr);
+ }
+ return (err_t)i;
+ }
+
+ /* mark a fresh entry as pending (we just sent a request) */
+ if (arp_table[i].state == ETHARP_STATE_EMPTY) {
+ is_new_entry = 1;
+ arp_table[i].state = ETHARP_STATE_PENDING;
+ /* record network interface for re-sending arp request in etharp_tmr */
+ arp_table[i].netif = netif;
+ }
+
+ /* { i is either a STABLE or (new or existing) PENDING entry } */
+ LWIP_ASSERT("arp_table[i].state == PENDING or STABLE",
+ ((arp_table[i].state == ETHARP_STATE_PENDING) ||
+ (arp_table[i].state >= ETHARP_STATE_STABLE)));
+
+ /* do we have a new entry? or an implicit query request? */
+ if (is_new_entry || (q == NULL)) {
+ /* try to resolve it; send out ARP request */
+ result = etharp_request(netif, ipaddr);
+ if (result != ERR_OK) {
+ /* ARP request couldn't be sent */
+ /* We don't re-send arp request in etharp_tmr, but we still queue packets,
+ since this failure could be temporary, and the next packet calling
+ etharp_query again could lead to sending the queued packets. */
+ }
+ if (q == NULL) {
+ return result;
+ }
+ }
+
+ /* packet given? */
+ LWIP_ASSERT("q != NULL", q != NULL);
+ /* stable entry? */
+ if (arp_table[i].state >= ETHARP_STATE_STABLE) {
+ /* we have a valid IP->Ethernet address mapping */
+ ETHARP_SET_HINT(netif, i);
+ /* send the packet */
+ result = etharp_send_ip(netif, q, srcaddr, &(arp_table[i].ethaddr));
+ /* pending entry? (either just created or already pending */
+ } else if (arp_table[i].state == ETHARP_STATE_PENDING) {
+ /* entry is still pending, queue the given packet 'q' */
+ struct pbuf *p;
+ int copy_needed = 0;
+ /* IF q includes a PBUF_REF, PBUF_POOL or PBUF_RAM, we have no choice but
+ * to copy the whole queue into a new PBUF_RAM (see bug #11400)
+ * PBUF_ROMs can be left as they are, since ROM must not get changed. */
+ p = q;
+ while (p) {
+ LWIP_ASSERT("no packet queues allowed!", (p->len != p->tot_len) || (p->next == 0));
+ if(p->type != PBUF_ROM) {
+ copy_needed = 1;
+ break;
+ }
+ p = p->next;
+ }
+ if(copy_needed) {
+ /* copy the whole packet into new pbufs */
+ p = pbuf_alloc(PBUF_RAW_TX, p->tot_len, PBUF_RAM);
+ if(p != NULL) {
+ if (pbuf_copy(p, q) != ERR_OK) {
+ pbuf_free(p);
+ p = NULL;
+ }
+ }
+ } else {
+ /* referencing the old pbuf is enough */
+ p = q;
+ pbuf_ref(p);
+ }
+ /* packet could be taken over? */
+ if (p != NULL) {
+ /* queue packet ... */
+#if ARP_QUEUEING
+ struct etharp_q_entry *new_entry;
+ /* allocate a new arp queue entry */
+ new_entry = (struct etharp_q_entry *)memp_malloc(MEMP_ARP_QUEUE);
+ if (new_entry != NULL) {
+ unsigned int qlen = 0;
+ new_entry->next = 0;
+ new_entry->p = p;
+ if(arp_table[i].q != NULL) {
+ /* queue was already existent, append the new entry to the end */
+ struct etharp_q_entry *r;
+ r = arp_table[i].q;
+ qlen++;
+ while (r->next != NULL) {
+ r = r->next;
+ qlen++;
+ }
+ r->next = new_entry;
+ } else {
+ /* queue did not exist, first item in queue */
+ arp_table[i].q = new_entry;
+ }
+#if ARP_QUEUE_LEN
+ if (qlen >= ARP_QUEUE_LEN) {
+ struct etharp_q_entry *old;
+ old = arp_table[i].q;
+ arp_table[i].q = arp_table[i].q->next;
+ pbuf_free(old->p);
+ memp_free(MEMP_ARP_QUEUE, old);
+ }
+#endif
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: queued packet %p on ARP entry %"S16_F"\n", (void *)q, (s16_t)i));
+ result = ERR_OK;
+ } else {
+ /* the pool MEMP_ARP_QUEUE is empty */
+ pbuf_free(p);
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: could not queue a copy of PBUF_REF packet %p (out of memory)\n", (void *)q));
+ result = ERR_MEM;
+ }
+#else /* ARP_QUEUEING */
+ /* always queue one packet per ARP request only, freeing a previously queued packet */
+ if (arp_table[i].q != NULL) {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: dropped previously queued packet %p for ARP entry %"S16_F"\n", (void *)q, (s16_t)i));
+ pbuf_free(arp_table[i].q);
+ }
+ arp_table[i].q = p;
+ result = ERR_OK;
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: queued packet %p on ARP entry %"S16_F"\n", (void *)q, (s16_t)i));
+#endif /* ARP_QUEUEING */
+ } else {
+ ETHARP_STATS_INC(etharp.memerr);
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: could not queue a copy of PBUF_REF packet %p (out of memory)\n", (void *)q));
+ result = ERR_MEM;
+ }
+ }
+ return result;
+}
+
+/**
+ * Send a raw ARP packet (opcode and all addresses can be modified)
+ *
+ * @param netif the lwip network interface on which to send the ARP packet
+ * @param ethsrc_addr the source MAC address for the ethernet header
+ * @param ethdst_addr the destination MAC address for the ethernet header
+ * @param hwsrc_addr the source MAC address for the ARP protocol header
+ * @param ipsrc_addr the source IP address for the ARP protocol header
+ * @param hwdst_addr the destination MAC address for the ARP protocol header
+ * @param ipdst_addr the destination IP address for the ARP protocol header
+ * @param opcode the type of the ARP packet
+ * @return ERR_OK if the ARP packet has been sent
+ * ERR_MEM if the ARP packet couldn't be allocated
+ * any other err_t on failure
+ */
+#if !LWIP_AUTOIP
+static
+#endif /* LWIP_AUTOIP */
+err_t
+etharp_raw(struct netif *netif, const struct eth_addr *ethsrc_addr,
+ const struct eth_addr *ethdst_addr,
+ const struct eth_addr *hwsrc_addr, const ip_addr_t *ipsrc_addr,
+ const struct eth_addr *hwdst_addr, const ip_addr_t *ipdst_addr,
+ const u16_t opcode)
+{
+ struct pbuf *p;
+ err_t result = ERR_OK;
+ struct eth_hdr *ethhdr;
+#if ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET)
+ struct eth_vlan_hdr *vlanhdr;
+#endif /* ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET) */
+ struct etharp_hdr *hdr;
+#if LWIP_AUTOIP
+ const u8_t * ethdst_hwaddr;
+#endif /* LWIP_AUTOIP */
+
+ LWIP_ASSERT("netif != NULL", netif != NULL);
+
+ /* allocate a pbuf for the outgoing ARP request packet */
+ p = pbuf_alloc(PBUF_RAW_TX, SIZEOF_ETHARP_PACKET_TX, PBUF_RAM);
+ /* could allocate a pbuf for an ARP request? */
+ if (p == NULL) {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
+ ("etharp_raw: could not allocate pbuf for ARP request.\n"));
+ ETHARP_STATS_INC(etharp.memerr);
+ return ERR_MEM;
+ }
+ LWIP_ASSERT("check that first pbuf can hold struct etharp_hdr",
+ (p->len >= SIZEOF_ETHARP_PACKET_TX));
+
+ ethhdr = (struct eth_hdr *)p->payload;
+#if ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET)
+ vlanhdr = (struct eth_vlan_hdr*)(((u8_t*)ethhdr) + SIZEOF_ETH_HDR);
+ hdr = (struct etharp_hdr *)((u8_t*)ethhdr + SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR);
+#else /* ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET) */
+ hdr = (struct etharp_hdr *)((u8_t*)ethhdr + SIZEOF_ETH_HDR);
+#endif /* ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET) */
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_raw: sending raw ARP packet.\n"));
+ hdr->opcode = htons(opcode);
+
+ LWIP_ASSERT("netif->hwaddr_len must be the same as ETHARP_HWADDR_LEN for etharp!",
+ (netif->hwaddr_len == ETHARP_HWADDR_LEN));
+#if LWIP_AUTOIP
+ /* If we are using Link-Local, all ARP packets that contain a Link-Local
+ * 'sender IP address' MUST be sent using link-layer broadcast instead of
+ * link-layer unicast. (See RFC3927 Section 2.5, last paragraph) */
+ ethdst_hwaddr = ip_addr_islinklocal(ipsrc_addr) ? (u8_t*)(ethbroadcast.addr) : ethdst_addr->addr;
+#endif /* LWIP_AUTOIP */
+ /* Write the ARP MAC-Addresses */
+ ETHADDR16_COPY(&hdr->shwaddr, hwsrc_addr);
+ ETHADDR16_COPY(&hdr->dhwaddr, hwdst_addr);
+ /* Copy struct ip_addr2 to aligned ip_addr, to support compilers without
+ * structure packing. */
+ IPADDR2_COPY(&hdr->sipaddr, ipsrc_addr);
+ IPADDR2_COPY(&hdr->dipaddr, ipdst_addr);
+
+ hdr->hwtype = PP_HTONS(HWTYPE_ETHERNET);
+ hdr->proto = PP_HTONS(ETHTYPE_IP);
+ /* set hwlen and protolen */
+ hdr->hwlen = ETHARP_HWADDR_LEN;
+ hdr->protolen = sizeof(ip_addr_t);
+
+#if ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET)
+ ethhdr->type = PP_HTONS(ETHTYPE_VLAN);
+ vlanhdr->tpid = PP_HTONS(ETHTYPE_ARP);
+ vlanhdr->prio_vid = 0;
+ if (!LWIP_HOOK_VLAN_SET(netif, ethhdr, vlanhdr)) {
+ /* packet shall not contain VLAN header, so hide it and set correct ethertype */
+ pbuf_header(p, -SIZEOF_VLAN_HDR);
+ ethhdr = (struct eth_hdr *)p->payload;
+#endif /* ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET) */
+ ethhdr->type = PP_HTONS(ETHTYPE_ARP);
+#if ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET)
+ }
+#endif /* ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET) */
+
+ /* Write the Ethernet MAC-Addresses */
+#if LWIP_AUTOIP
+ ETHADDR16_COPY(&ethhdr->dest, ethdst_hwaddr);
+#else /* LWIP_AUTOIP */
+ ETHADDR16_COPY(&ethhdr->dest, ethdst_addr);
+#endif /* LWIP_AUTOIP */
+ ETHADDR16_COPY(&ethhdr->src, ethsrc_addr);
+
+ /* send ARP query */
+ result = netif->linkoutput(netif, p);
+ ETHARP_STATS_INC(etharp.xmit);
+ /* free ARP query packet */
+ pbuf_free(p);
+ p = NULL;
+ /* could not allocate pbuf for ARP request */
+
+ return result;
+}
+
+/**
+ * Send an ARP request packet asking for ipaddr to a specific eth address.
+ * Used to send unicast request to refresh the ARP table just before an entry
+ * times out
+ *
+ * @param netif the lwip network interface on which to send the request
+ * @param ipaddr the IP address for which to ask
+ * @param hw_dst_addr the ethernet address to send this packet to
+ * @return ERR_OK if the request has been sent
+ * ERR_MEM if the ARP packet couldn't be allocated
+ * any other err_t on failure
+ */
+static err_t
+etharp_request_dst(struct netif *netif, const ip_addr_t *ipaddr, const struct eth_addr* hw_dst_addr)
+{
+ return etharp_raw(netif, (struct eth_addr *)netif->hwaddr, hw_dst_addr,
+ (struct eth_addr *)netif->hwaddr, &netif->ip_addr, &ethzero,
+ ipaddr, ARP_REQUEST);
+}
+
+/**
+ * Send an ARP request packet asking for ipaddr.
+ *
+ * @param netif the lwip network interface on which to send the request
+ * @param ipaddr the IP address for which to ask
+ * @return ERR_OK if the request has been sent
+ * ERR_MEM if the ARP packet couldn't be allocated
+ * any other err_t on failure
+ */
+err_t
+etharp_request(struct netif *netif, const ip_addr_t *ipaddr)
+{
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_request: sending ARP request.\n"));
+ return etharp_request_dst(netif, ipaddr, &ethbroadcast);
+}
+#endif /* LWIP_ARP */
+
+/**
+ * Process received ethernet frames. Using this function instead of directly
+ * calling ip_input and passing ARP frames through etharp in ethernetif_input,
+ * the ARP cache is protected from concurrent access.
+ *
+ * @param p the received packet, p->payload pointing to the ethernet header
+ * @param netif the network interface on which the packet was received
+ */
+err_t
+ethernet_input(struct pbuf *p, struct netif *netif)
+{
+ struct eth_hdr* ethhdr;
+ u16_t type;
+#if LWIP_ARP || ETHARP_SUPPORT_VLAN || LWIP_IPV6
+ s16_t ip_hdr_offset = SIZEOF_ETH_HDR;
+#endif /* LWIP_ARP || ETHARP_SUPPORT_VLAN */
+
+ if (p->len <= SIZEOF_ETH_HDR) {
+ /* a packet with only an ethernet header (or less) is not valid for us */
+ ETHARP_STATS_INC(etharp.proterr);
+ ETHARP_STATS_INC(etharp.drop);
+ goto free_and_return;
+ }
+
+ /* points to packet payload, which starts with an Ethernet header */
+ ethhdr = (struct eth_hdr *)p->payload;
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE,
+ ("ethernet_input: dest:%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F", src:%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F", type:%"X16_F"\n",
+ (unsigned)ethhdr->dest.addr[0], (unsigned)ethhdr->dest.addr[1], (unsigned)ethhdr->dest.addr[2],
+ (unsigned)ethhdr->dest.addr[3], (unsigned)ethhdr->dest.addr[4], (unsigned)ethhdr->dest.addr[5],
+ (unsigned)ethhdr->src.addr[0], (unsigned)ethhdr->src.addr[1], (unsigned)ethhdr->src.addr[2],
+ (unsigned)ethhdr->src.addr[3], (unsigned)ethhdr->src.addr[4], (unsigned)ethhdr->src.addr[5],
+ (unsigned)htons(ethhdr->type)));
+
+ type = ethhdr->type;
+#if ETHARP_SUPPORT_VLAN
+ if (type == PP_HTONS(ETHTYPE_VLAN)) {
+ struct eth_vlan_hdr *vlan = (struct eth_vlan_hdr*)(((char*)ethhdr) + SIZEOF_ETH_HDR);
+ if (p->len <= SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR) {
+ /* a packet with only an ethernet/vlan header (or less) is not valid for us */
+ ETHARP_STATS_INC(etharp.proterr);
+ ETHARP_STATS_INC(etharp.drop);
+ goto free_and_return;
+ }
+#if defined(LWIP_HOOK_VLAN_CHECK) || defined(ETHARP_VLAN_CHECK) || defined(ETHARP_VLAN_CHECK_FN) /* if not, allow all VLANs */
+#ifdef LWIP_HOOK_VLAN_CHECK
+ if (!LWIP_HOOK_VLAN_CHECK(netif, ethhdr, vlan)) {
+#elif defined(ETHARP_VLAN_CHECK_FN)
+ if (!ETHARP_VLAN_CHECK_FN(ethhdr, vlan)) {
+#elif defined(ETHARP_VLAN_CHECK)
+ if (VLAN_ID(vlan) != ETHARP_VLAN_CHECK) {
+#endif
+ /* silently ignore this packet: not for our VLAN */
+ pbuf_free(p);
+ return ERR_OK;
+ }
+#endif /* defined(LWIP_HOOK_VLAN_CHECK) || defined(ETHARP_VLAN_CHECK) || defined(ETHARP_VLAN_CHECK_FN) */
+ type = vlan->tpid;
+ ip_hdr_offset = SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR;
+ }
+#endif /* ETHARP_SUPPORT_VLAN */
+
+#if LWIP_ARP_FILTER_NETIF
+ netif = LWIP_ARP_FILTER_NETIF_FN(p, netif, htons(type));
+#endif /* LWIP_ARP_FILTER_NETIF*/
+
+ if (ethhdr->dest.addr[0] & 1) {
+ /* this might be a multicast or broadcast packet */
+ if (ethhdr->dest.addr[0] == LL_MULTICAST_ADDR_0) {
+ if ((ethhdr->dest.addr[1] == LL_MULTICAST_ADDR_1) &&
+ (ethhdr->dest.addr[2] == LL_MULTICAST_ADDR_2)) {
+ /* mark the pbuf as link-layer multicast */
+ p->flags |= PBUF_FLAG_LLMCAST;
+ }
+ } else if (eth_addr_cmp(&ethhdr->dest, &ethbroadcast)) {
+ /* mark the pbuf as link-layer broadcast */
+ p->flags |= PBUF_FLAG_LLBCAST;
+ }
+ }
+
+ switch (type) {
+#if LWIP_ARP
+ /* IP packet? */
+ case PP_HTONS(ETHTYPE_IP):
+ if (!(netif->flags & NETIF_FLAG_ETHARP)) {
+ goto free_and_return;
+ }
+#if ETHARP_TRUST_IP_MAC
+ /* update ARP table */
+ etharp_ip_input(netif, p);
+#endif /* ETHARP_TRUST_IP_MAC */
+ /* skip Ethernet header */
+ if ((p->len < ip_hdr_offset) || pbuf_header(p, (s16_t)-ip_hdr_offset)) {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING,
+ ("ethernet_input: IPv4 packet dropped, too short (%"S16_F"/%"S16_F")\n",
+ p->tot_len, ip_hdr_offset));
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("Can't move over header in packet"));
+ goto free_and_return;
+ } else {
+ /* pass to IP layer */
+ ip_input(p, netif);
+ }
+ break;
+
+ case PP_HTONS(ETHTYPE_ARP):
+ if (!(netif->flags & NETIF_FLAG_ETHARP)) {
+ goto free_and_return;
+ }
+ /* pass p to ARP module */
+ etharp_arp_input(netif, (struct eth_addr*)(netif->hwaddr), p);
+ break;
+#endif /* LWIP_ARP */
+#if PPPOE_SUPPORT
+ case PP_HTONS(ETHTYPE_PPPOEDISC): /* PPP Over Ethernet Discovery Stage */
+ pppoe_disc_input(netif, p);
+ break;
+
+ case PP_HTONS(ETHTYPE_PPPOE): /* PPP Over Ethernet Session Stage */
+ pppoe_data_input(netif, p);
+ break;
+#endif /* PPPOE_SUPPORT */
+
+#if LWIP_IPV6
+ case PP_HTONS(ETHTYPE_IPV6): /* IPv6 */
+ /* skip Ethernet header */
+ if((p->len < ip_hdr_offset) || pbuf_header(p, (s16_t)-ip_hdr_offset)) {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING,
+ ("ethernet_input: IPv6 packet dropped, too short (%"S16_F"/%"S16_F")\n",
+ p->tot_len, ip_hdr_offset));
+ goto free_and_return;
+ } else {
+ /* pass to IPv6 layer */
+ ip6_input(p, netif);
+ }
+ break;
+#endif /* LWIP_IPV6 */
+
+ default:
+ ETHARP_STATS_INC(etharp.proterr);
+ ETHARP_STATS_INC(etharp.drop);
+ goto free_and_return;
+ }
+
+ /* This means the pbuf is freed or consumed,
+ so the caller doesn't have to free it again */
+ return ERR_OK;
+
+free_and_return:
+ pbuf_free(p);
+ return ERR_OK;
+}
+#endif /* LWIP_ARP || LWIP_ETHERNET */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ethernet.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ethernet.c
new file mode 100644
index 0000000..52ea423
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ethernet.c
@@ -0,0 +1,314 @@
+/**
+ * @file
+ * Ethernet common functions
+ *
+ * @defgroup ethernet Ethernet
+ * @ingroup callbackstyle_api
+ */
+
+/*
+ * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
+ * Copyright (c) 2003-2004 Leon Woestenberg <leon.woestenberg@axon.tv>
+ * Copyright (c) 2003-2004 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_ARP || LWIP_ETHERNET
+
+#include "netif/ethernet.h"
+#include "lwip/def.h"
+#include "lwip/stats.h"
+#include "lwip/etharp.h"
+#include "lwip/ip.h"
+#include "lwip/snmp.h"
+
+#include <string.h>
+
+#include "netif/ppp/ppp_opts.h"
+#if PPPOE_SUPPORT
+#include "netif/ppp/pppoe.h"
+#endif /* PPPOE_SUPPORT */
+
+#ifdef LWIP_HOOK_FILENAME
+#include LWIP_HOOK_FILENAME
+#endif
+
+const struct eth_addr ethbroadcast = {{0xff,0xff,0xff,0xff,0xff,0xff}};
+const struct eth_addr ethzero = {{0,0,0,0,0,0}};
+
+/**
+ * @ingroup lwip_nosys
+ * Process received ethernet frames. Using this function instead of directly
+ * calling ip_input and passing ARP frames through etharp in ethernetif_input,
+ * the ARP cache is protected from concurrent access.\n
+ * Don't call directly, pass to netif_add() and call netif->input().
+ *
+ * @param p the received packet, p->payload pointing to the ethernet header
+ * @param netif the network interface on which the packet was received
+ *
+ * @see LWIP_HOOK_UNKNOWN_ETH_PROTOCOL
+ * @see ETHARP_SUPPORT_VLAN
+ * @see LWIP_HOOK_VLAN_CHECK
+ */
+err_t
+ethernet_input(struct pbuf *p, struct netif *netif)
+{
+ struct eth_hdr* ethhdr;
+ u16_t type;
+#if LWIP_ARP || ETHARP_SUPPORT_VLAN || LWIP_IPV6
+ s16_t ip_hdr_offset = SIZEOF_ETH_HDR;
+#endif /* LWIP_ARP || ETHARP_SUPPORT_VLAN */
+
+ if (p->len <= SIZEOF_ETH_HDR) {
+ /* a packet with only an ethernet header (or less) is not valid for us */
+ ETHARP_STATS_INC(etharp.proterr);
+ ETHARP_STATS_INC(etharp.drop);
+ MIB2_STATS_NETIF_INC(netif, ifinerrors);
+ goto free_and_return;
+ }
+
+ /* points to packet payload, which starts with an Ethernet header */
+ ethhdr = (struct eth_hdr *)p->payload;
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE,
+ ("ethernet_input: dest:%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F", src:%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F", type:%"X16_F"\n",
+ (unsigned)ethhdr->dest.addr[0], (unsigned)ethhdr->dest.addr[1], (unsigned)ethhdr->dest.addr[2],
+ (unsigned)ethhdr->dest.addr[3], (unsigned)ethhdr->dest.addr[4], (unsigned)ethhdr->dest.addr[5],
+ (unsigned)ethhdr->src.addr[0], (unsigned)ethhdr->src.addr[1], (unsigned)ethhdr->src.addr[2],
+ (unsigned)ethhdr->src.addr[3], (unsigned)ethhdr->src.addr[4], (unsigned)ethhdr->src.addr[5],
+ lwip_htons(ethhdr->type)));
+
+ type = ethhdr->type;
+#if ETHARP_SUPPORT_VLAN
+ if (type == PP_HTONS(ETHTYPE_VLAN)) {
+ struct eth_vlan_hdr *vlan = (struct eth_vlan_hdr*)(((char*)ethhdr) + SIZEOF_ETH_HDR);
+ if (p->len <= SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR) {
+ /* a packet with only an ethernet/vlan header (or less) is not valid for us */
+ ETHARP_STATS_INC(etharp.proterr);
+ ETHARP_STATS_INC(etharp.drop);
+ MIB2_STATS_NETIF_INC(netif, ifinerrors);
+ goto free_and_return;
+ }
+#if defined(LWIP_HOOK_VLAN_CHECK) || defined(ETHARP_VLAN_CHECK) || defined(ETHARP_VLAN_CHECK_FN) /* if not, allow all VLANs */
+#ifdef LWIP_HOOK_VLAN_CHECK
+ if (!LWIP_HOOK_VLAN_CHECK(netif, ethhdr, vlan)) {
+#elif defined(ETHARP_VLAN_CHECK_FN)
+ if (!ETHARP_VLAN_CHECK_FN(ethhdr, vlan)) {
+#elif defined(ETHARP_VLAN_CHECK)
+ if (VLAN_ID(vlan) != ETHARP_VLAN_CHECK) {
+#endif
+ /* silently ignore this packet: not for our VLAN */
+ pbuf_free(p);
+ return ERR_OK;
+ }
+#endif /* defined(LWIP_HOOK_VLAN_CHECK) || defined(ETHARP_VLAN_CHECK) || defined(ETHARP_VLAN_CHECK_FN) */
+ type = vlan->tpid;
+ ip_hdr_offset = SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR;
+ }
+#endif /* ETHARP_SUPPORT_VLAN */
+
+#if LWIP_ARP_FILTER_NETIF
+ netif = LWIP_ARP_FILTER_NETIF_FN(p, netif, lwip_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_IP4_MULTICAST_ADDR_0) {
+#if LWIP_IPV4
+ if ((ethhdr->dest.addr[1] == LL_IP4_MULTICAST_ADDR_1) &&
+ (ethhdr->dest.addr[2] == LL_IP4_MULTICAST_ADDR_2)) {
+ /* mark the pbuf as link-layer multicast */
+ p->flags |= PBUF_FLAG_LLMCAST;
+ }
+#endif /* LWIP_IPV4 */
+ }
+#if LWIP_IPV6
+ else if ((ethhdr->dest.addr[0] == LL_IP6_MULTICAST_ADDR_0) &&
+ (ethhdr->dest.addr[1] == LL_IP6_MULTICAST_ADDR_1)) {
+ /* mark the pbuf as link-layer multicast */
+ p->flags |= PBUF_FLAG_LLMCAST;
+ }
+#endif /* LWIP_IPV6 */
+ else if (eth_addr_cmp(&ethhdr->dest, &ethbroadcast)) {
+ /* mark the pbuf as link-layer broadcast */
+ p->flags |= PBUF_FLAG_LLBCAST;
+ }
+ }
+
+ switch (type) {
+#if LWIP_IPV4 && LWIP_ARP
+ /* IP packet? */
+ case PP_HTONS(ETHTYPE_IP):
+ if (!(netif->flags & NETIF_FLAG_ETHARP)) {
+ goto free_and_return;
+ }
+ /* skip Ethernet header */
+ if ((p->len < ip_hdr_offset) || pbuf_header(p, (s16_t)-ip_hdr_offset)) {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING,
+ ("ethernet_input: IPv4 packet dropped, too short (%"S16_F"/%"S16_F")\n",
+ p->tot_len, ip_hdr_offset));
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("Can't move over header in packet"));
+ goto free_and_return;
+ } else {
+ /* pass to IP layer */
+ ip4_input(p, netif);
+ }
+ break;
+
+ case PP_HTONS(ETHTYPE_ARP):
+ if (!(netif->flags & NETIF_FLAG_ETHARP)) {
+ goto free_and_return;
+ }
+ /* skip Ethernet header */
+ if ((p->len < ip_hdr_offset) || pbuf_header(p, (s16_t)-ip_hdr_offset)) {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING,
+ ("ethernet_input: ARP response packet dropped, too short (%"S16_F"/%"S16_F")\n",
+ p->tot_len, ip_hdr_offset));
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("Can't move over header in packet"));
+ ETHARP_STATS_INC(etharp.lenerr);
+ ETHARP_STATS_INC(etharp.drop);
+ goto free_and_return;
+ } else {
+ /* pass p to ARP module */
+ etharp_input(p, netif);
+ }
+ break;
+#endif /* LWIP_IPV4 && LWIP_ARP */
+#if PPPOE_SUPPORT
+ case PP_HTONS(ETHTYPE_PPPOEDISC): /* PPP Over Ethernet Discovery Stage */
+ pppoe_disc_input(netif, p);
+ break;
+
+ case PP_HTONS(ETHTYPE_PPPOE): /* PPP Over Ethernet Session Stage */
+ pppoe_data_input(netif, p);
+ break;
+#endif /* PPPOE_SUPPORT */
+
+#if LWIP_IPV6
+ case PP_HTONS(ETHTYPE_IPV6): /* IPv6 */
+ /* skip Ethernet header */
+ if ((p->len < ip_hdr_offset) || pbuf_header(p, (s16_t)-ip_hdr_offset)) {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING,
+ ("ethernet_input: IPv6 packet dropped, too short (%"S16_F"/%"S16_F")\n",
+ p->tot_len, ip_hdr_offset));
+ goto free_and_return;
+ } else {
+ /* pass to IPv6 layer */
+ ip6_input(p, netif);
+ }
+ break;
+#endif /* LWIP_IPV6 */
+
+ default:
+#ifdef LWIP_HOOK_UNKNOWN_ETH_PROTOCOL
+ if(LWIP_HOOK_UNKNOWN_ETH_PROTOCOL(p, netif) == ERR_OK) {
+ break;
+ }
+#endif
+ ETHARP_STATS_INC(etharp.proterr);
+ ETHARP_STATS_INC(etharp.drop);
+ MIB2_STATS_NETIF_INC(netif, ifinunknownprotos);
+ goto free_and_return;
+ }
+
+ /* This means the pbuf is freed or consumed,
+ so the caller doesn't have to free it again */
+ return ERR_OK;
+
+free_and_return:
+ pbuf_free(p);
+ return ERR_OK;
+}
+
+/**
+ * @ingroup ethernet
+ * Send an ethernet packet on the network using netif->linkoutput().
+ * The ethernet header is filled in before sending.
+ *
+ * @see LWIP_HOOK_VLAN_SET
+ *
+ * @param netif the lwIP network interface on which to send the packet
+ * @param p the packet to send. pbuf layer must be @ref PBUF_LINK.
+ * @param src the source MAC address to be copied into the ethernet header
+ * @param dst the destination MAC address to be copied into the ethernet header
+ * @param eth_type ethernet type (@ref eth_type)
+ * @return ERR_OK if the packet was sent, any other err_t on failure
+ */
+err_t
+ethernet_output(struct netif* netif, struct pbuf* p,
+ const struct eth_addr* src, const struct eth_addr* dst,
+ u16_t eth_type)
+{
+ struct eth_hdr* ethhdr;
+ u16_t eth_type_be = lwip_htons(eth_type);
+
+#if ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET)
+ s32_t vlan_prio_vid = LWIP_HOOK_VLAN_SET(netif, p, src, dst, eth_type);
+ if (vlan_prio_vid >= 0) {
+ struct eth_vlan_hdr* vlanhdr;
+
+ LWIP_ASSERT("prio_vid must be <= 0xFFFF", vlan_prio_vid <= 0xFFFF);
+
+ if (pbuf_header(p, SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR) != 0) {
+ goto pbuf_header_failed;
+ }
+ vlanhdr = (struct eth_vlan_hdr*)(((u8_t*)p->payload) + SIZEOF_ETH_HDR);
+ vlanhdr->tpid = eth_type_be;
+ vlanhdr->prio_vid = lwip_htons((u16_t)vlan_prio_vid);
+
+ eth_type_be = PP_HTONS(ETHTYPE_VLAN);
+ } else
+#endif /* ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET) */
+ {
+ if (pbuf_header(p, SIZEOF_ETH_HDR) != 0) {
+ goto pbuf_header_failed;
+ }
+ }
+
+ ethhdr = (struct eth_hdr*)p->payload;
+ ethhdr->type = eth_type_be;
+ ETHADDR32_COPY(&ethhdr->dest, dst);
+ ETHADDR16_COPY(&ethhdr->src, src);
+
+ LWIP_ASSERT("netif->hwaddr_len must be 6 for ethernet_output!",
+ (netif->hwaddr_len == ETH_HWADDR_LEN));
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE,
+ ("ethernet_output: sending packet %p\n", (void *)p));
+
+ /* send the packet */
+ return netif->linkoutput(netif, p);
+
+pbuf_header_failed:
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
+ ("ethernet_output: could not allocate room for header.\n"));
+ LINK_STATS_INC(link.lenerr);
+ return ERR_BUF;
+}
+
+#endif /* LWIP_ARP || LWIP_ETHERNET */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ethernetif.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ethernetif.c
new file mode 100644
index 0000000..dc8ae6c
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ethernetif.c
@@ -0,0 +1,335 @@
+/**
+ * @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 "lwip/ethip6.h"
+#include "lwip/etharp.h"
+#include "netif/ppp/pppoe.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;
+
+#if LWIP_IPV6 && LWIP_IPV6_MLD
+ /*
+ * For hardware/netifs that implement MAC filtering.
+ * All-nodes link-local is handled by default, so we must let the hardware know
+ * to allow multicast packets in.
+ * Should set mld_mac_filter previously. */
+ if (netif->mld_mac_filter != NULL) {
+ ip6_addr_t ip6_allnodes_ll;
+ ip6_addr_set_allnodes_linklocal(&ip6_allnodes_ll);
+ netif->mld_mac_filter(netif, &ip6_allnodes_ll, NETIF_ADD_MAC_FILTER);
+ }
+#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */
+
+ /* 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 available 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();
+
+ MIB2_STATS_NETIF_ADD(netif, ifoutoctets, p->tot_len);
+ if (((u8_t*)p->payload)[0] & 1) {
+ /* broadcast or multicast packet*/
+ MIB2_STATS_NETIF_INC(netif, ifoutnucastpkts);
+ } else {
+ /* unicast packet */
+ MIB2_STATS_NETIF_INC(netif, ifoutucastpkts);
+ }
+ /* increase ifoutdiscards or ifouterrors on error */
+
+#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();
+
+ MIB2_STATS_NETIF_ADD(netif, ifinoctets, p->tot_len);
+ if (((u8_t*)p->payload)[0] & 1) {
+ /* broadcast or multicast packet*/
+ MIB2_STATS_NETIF_INC(netif, ifinnucastpkts);
+ } else {
+ /* unicast packet*/
+ MIB2_STATS_NETIF_INC(netif, ifinucastpkts);
+ }
+#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);
+ MIB2_STATS_NETIF_INC(netif, ifindiscards);
+ }
+
+ 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);
+ /* if no packet could be read, silently ignore this */
+ if (p != NULL) {
+ /* pass all packets to ethernet_input, which decides what packets it supports */
+ if (netif->input(p, netif) != ERR_OK) {
+ LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_input: IP input error\n"));
+ pbuf_free(p);
+ p = NULL;
+ }
+ }
+}
+
+/**
+ * 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.
+ */
+ MIB2_INIT_NETIF(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;
+#if LWIP_IPV6
+ netif->output_ip6 = ethip6_output;
+#endif /* LWIP_IPV6 */
+ 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/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/lowpan6.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/lowpan6.c
new file mode 100644
index 0000000..9a84cbc
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/lowpan6.c
@@ -0,0 +1,1193 @@
+/**
+ * @file
+ *
+ * 6LowPAN output for IPv6. Uses ND tables for link-layer addressing. Fragments packets to 6LowPAN units.
+ */
+
+/*
+ * Copyright (c) 2015 Inico Technologies Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Ivan Delamer <delamer@inicotech.com>
+ *
+ *
+ * Please coordinate changes and requests with Ivan Delamer
+ * <delamer@inicotech.com>
+ */
+
+/**
+ * @defgroup sixlowpan 6LowPAN netif
+ * @ingroup addons
+ * 6LowPAN netif implementation
+ */
+
+#include "netif/lowpan6.h"
+
+#if LWIP_IPV6 && LWIP_6LOWPAN
+
+#include "lwip/ip.h"
+#include "lwip/pbuf.h"
+#include "lwip/ip_addr.h"
+#include "lwip/netif.h"
+#include "lwip/nd6.h"
+#include "lwip/mem.h"
+#include "lwip/udp.h"
+#include "lwip/tcpip.h"
+#include "lwip/snmp.h"
+
+#include <string.h>
+
+struct ieee_802154_addr {
+ u8_t addr_len;
+ u8_t addr[8];
+};
+
+/** This is a helper struct.
+ */
+struct lowpan6_reass_helper {
+ struct pbuf *pbuf;
+ struct lowpan6_reass_helper *next_packet;
+ u8_t timer;
+ struct ieee_802154_addr sender_addr;
+ u16_t datagram_size;
+ u16_t datagram_tag;
+};
+
+static struct lowpan6_reass_helper * reass_list;
+
+#if LWIP_6LOWPAN_NUM_CONTEXTS > 0
+static ip6_addr_t lowpan6_context[LWIP_6LOWPAN_NUM_CONTEXTS];
+#endif
+
+static u16_t ieee_802154_pan_id;
+
+static const struct ieee_802154_addr ieee_802154_broadcast = {2, {0xff, 0xff}};
+
+#if LWIP_6LOWPAN_INFER_SHORT_ADDRESS
+static struct ieee_802154_addr short_mac_addr = {2, {0,0}};
+#endif /* LWIP_6LOWPAN_INFER_SHORT_ADDRESS */
+
+static err_t dequeue_datagram(struct lowpan6_reass_helper *lrh);
+
+/**
+ * Periodic timer for 6LowPAN functions:
+ *
+ * - Remove incomplete/old packets
+ */
+void
+lowpan6_tmr(void)
+{
+ struct lowpan6_reass_helper *lrh, *lrh_temp;
+
+ lrh = reass_list;
+ while (lrh != NULL) {
+ lrh_temp = lrh->next_packet;
+ if ((--lrh->timer) == 0) {
+ dequeue_datagram(lrh);
+ pbuf_free(lrh->pbuf);
+ mem_free(lrh);
+ }
+ lrh = lrh_temp;
+ }
+}
+
+/**
+ * Removes a datagram from the reassembly queue.
+ **/
+static err_t
+dequeue_datagram(struct lowpan6_reass_helper *lrh)
+{
+ struct lowpan6_reass_helper *lrh_temp;
+
+ if (reass_list == lrh) {
+ reass_list = reass_list->next_packet;
+ } else {
+ lrh_temp = reass_list;
+ while (lrh_temp != NULL) {
+ if (lrh_temp->next_packet == lrh) {
+ lrh_temp->next_packet = lrh->next_packet;
+ break;
+ }
+ lrh_temp = lrh_temp->next_packet;
+ }
+ }
+
+ return ERR_OK;
+}
+
+static s8_t
+lowpan6_context_lookup(const ip6_addr_t *ip6addr)
+{
+ s8_t i;
+
+ for (i = 0; i < LWIP_6LOWPAN_NUM_CONTEXTS; i++) {
+ if (ip6_addr_netcmp(&lowpan6_context[i], ip6addr)) {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+/* Determine compression mode for unicast address. */
+static s8_t
+lowpan6_get_address_mode(const ip6_addr_t *ip6addr, const struct ieee_802154_addr *mac_addr)
+{
+ if (mac_addr->addr_len == 2) {
+ if ((ip6addr->addr[2] == (u32_t)PP_HTONL(0x000000ff)) &&
+ ((ip6addr->addr[3] & PP_HTONL(0xffff0000)) == PP_NTOHL(0xfe000000))) {
+ if ((ip6addr->addr[3] & PP_HTONL(0x0000ffff)) == lwip_ntohl((mac_addr->addr[0] << 8) | mac_addr->addr[1])) {
+ return 3;
+ }
+ }
+ } else if (mac_addr->addr_len == 8) {
+ if ((ip6addr->addr[2] == lwip_ntohl(((mac_addr->addr[0] ^ 2) << 24) | (mac_addr->addr[1] << 16) | mac_addr->addr[2] << 8 | mac_addr->addr[3])) &&
+ (ip6addr->addr[3] == lwip_ntohl((mac_addr->addr[4] << 24) | (mac_addr->addr[5] << 16) | mac_addr->addr[6] << 8 | mac_addr->addr[7]))) {
+ return 3;
+ }
+ }
+
+ if ((ip6addr->addr[2] == PP_HTONL(0x000000ffUL)) &&
+ ((ip6addr->addr[3] & PP_HTONL(0xffff0000)) == PP_NTOHL(0xfe000000UL))) {
+ return 2;
+ }
+
+ return 1;
+}
+
+/* Determine compression mode for multicast address. */
+static s8_t
+lowpan6_get_address_mode_mc(const ip6_addr_t *ip6addr)
+{
+ if ((ip6addr->addr[0] == PP_HTONL(0xff020000)) &&
+ (ip6addr->addr[1] == 0) &&
+ (ip6addr->addr[2] == 0) &&
+ ((ip6addr->addr[3] & PP_HTONL(0xffffff00)) == 0)) {
+ return 3;
+ } else if (((ip6addr->addr[0] & PP_HTONL(0xff00ffff)) == PP_HTONL(0xff000000)) &&
+ (ip6addr->addr[1] == 0)) {
+ if ((ip6addr->addr[2] == 0) &&
+ ((ip6addr->addr[3] & PP_HTONL(0xff000000)) == 0)) {
+ return 2;
+ } else if ((ip6addr->addr[2] & PP_HTONL(0xffffff00)) == 0) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Encapsulates data into IEEE 802.15.4 frames.
+ * Fragments an IPv6 datagram into 6LowPAN units, which fit into IEEE 802.15.4 frames.
+ * If configured, will compress IPv6 and or UDP headers.
+ * */
+static err_t
+lowpan6_frag(struct netif *netif, struct pbuf *p, const struct ieee_802154_addr *src, const struct ieee_802154_addr *dst)
+{
+ struct pbuf * p_frag;
+ u16_t frag_len, remaining_len;
+ u8_t * buffer;
+ u8_t ieee_header_len;
+ u8_t lowpan6_header_len;
+ s8_t i;
+ static u8_t frame_seq_num;
+ static u16_t datagram_tag;
+ u16_t datagram_offset;
+ err_t err = ERR_IF;
+
+ /* We'll use a dedicated pbuf for building 6LowPAN fragments. */
+ p_frag = pbuf_alloc(PBUF_RAW, 127, PBUF_RAM);
+ if (p_frag == NULL) {
+ MIB2_STATS_NETIF_INC(netif, ifoutdiscards);
+ return ERR_MEM;
+ }
+
+ /* Write IEEE 802.15.4 header. */
+ buffer = (u8_t*)p_frag->payload;
+ ieee_header_len = 0;
+ if (dst == &ieee_802154_broadcast) {
+ buffer[ieee_header_len++] = 0x01; /* data packet, no ack required. */
+ } else {
+ buffer[ieee_header_len++] = 0x21; /* data packet, ack required. */
+ }
+ buffer[ieee_header_len] = (0x00 << 4); /* 2003 frame version */
+ buffer[ieee_header_len] |= (dst->addr_len == 2) ? (0x02 << 2) : (0x03 << 2); /* destination addressing mode */
+ buffer[ieee_header_len] |= (src->addr_len == 2) ? (0x02 << 6) : (0x03 << 6); /* source addressing mode */
+ ieee_header_len++;
+ buffer[ieee_header_len++] = frame_seq_num++;
+
+ buffer[ieee_header_len++] = ieee_802154_pan_id & 0xff; /* pan id */
+ buffer[ieee_header_len++] = (ieee_802154_pan_id >> 8) & 0xff; /* pan id */
+ i = dst->addr_len;
+ while (i-- > 0) {
+ buffer[ieee_header_len++] = dst->addr[i];
+ }
+
+ buffer[ieee_header_len++] = ieee_802154_pan_id & 0xff; /* pan id */
+ buffer[ieee_header_len++] = (ieee_802154_pan_id >> 8) & 0xff; /* pan id */
+ i = src->addr_len;
+ while (i-- > 0) {
+ buffer[ieee_header_len++] = src->addr[i];
+ }
+
+#if LWIP_6LOWPAN_IPHC
+ /* Perform 6LowPAN IPv6 header compression according to RFC 6282 */
+ {
+ struct ip6_hdr *ip6hdr;
+
+ /* Point to ip6 header and align copies of src/dest addresses. */
+ ip6hdr = (struct ip6_hdr *)p->payload;
+ ip_addr_copy_from_ip6(ip_data.current_iphdr_dest, ip6hdr->dest);
+ ip_addr_copy_from_ip6(ip_data.current_iphdr_src, ip6hdr->src);
+
+ /* Basic length of 6LowPAN header, set dispatch and clear fields. */
+ lowpan6_header_len = 2;
+ buffer[ieee_header_len] = 0x60;
+ buffer[ieee_header_len + 1] = 0;
+
+ /* Determine whether there will be a Context Identifier Extension byte or not.
+ * If so, set it already. */
+#if LWIP_6LOWPAN_NUM_CONTEXTS > 0
+ buffer[ieee_header_len + 2] = 0;
+
+ i = lowpan6_context_lookup(ip_2_ip6(&ip_data.current_iphdr_src));
+ if (i >= 0) {
+ /* Stateful source address compression. */
+ buffer[ieee_header_len + 1] |= 0x40;
+ buffer[ieee_header_len + 2] |= (i & 0x0f) << 4;
+ }
+
+ i = lowpan6_context_lookup(ip_2_ip6(&ip_data.current_iphdr_dest));
+ if (i >= 0) {
+ /* Stateful destination address compression. */
+ buffer[ieee_header_len + 1] |= 0x04;
+ buffer[ieee_header_len + 2] |= i & 0x0f;
+ }
+
+ if (buffer[ieee_header_len + 2] != 0x00) {
+ /* Context identifier extension byte is appended. */
+ buffer[ieee_header_len + 1] |= 0x80;
+ lowpan6_header_len++;
+ }
+#endif /* LWIP_6LOWPAN_NUM_CONTEXTS > 0 */
+
+ /* Determine TF field: Traffic Class, Flow Label */
+ if (IP6H_FL(ip6hdr) == 0) {
+ /* Flow label is elided. */
+ buffer[ieee_header_len] |= 0x10;
+ if (IP6H_TC(ip6hdr) == 0) {
+ /* Traffic class (ECN+DSCP) elided too. */
+ buffer[ieee_header_len] |= 0x08;
+ } else {
+ /* Traffic class (ECN+DSCP) appended. */
+ buffer[ieee_header_len + lowpan6_header_len++] = IP6H_TC(ip6hdr);
+ }
+ } else {
+ if (((IP6H_TC(ip6hdr) & 0x3f) == 0)) {
+ /* DSCP portion of Traffic Class is elided, ECN and FL are appended (3 bytes) */
+ buffer[ieee_header_len] |= 0x08;
+
+ buffer[ieee_header_len + lowpan6_header_len] = IP6H_TC(ip6hdr) & 0xc0;
+ buffer[ieee_header_len + lowpan6_header_len++] |= (IP6H_FL(ip6hdr) >> 16) & 0x0f;
+ buffer[ieee_header_len + lowpan6_header_len++] = (IP6H_FL(ip6hdr) >> 8) & 0xff;
+ buffer[ieee_header_len + lowpan6_header_len++] = IP6H_FL(ip6hdr) & 0xff;
+ } else {
+ /* Traffic class and flow label are appended (4 bytes) */
+ buffer[ieee_header_len + lowpan6_header_len++] = IP6H_TC(ip6hdr);
+ buffer[ieee_header_len + lowpan6_header_len++] = (IP6H_FL(ip6hdr) >> 16) & 0x0f;
+ buffer[ieee_header_len + lowpan6_header_len++] = (IP6H_FL(ip6hdr) >> 8) & 0xff;
+ buffer[ieee_header_len + lowpan6_header_len++] = IP6H_FL(ip6hdr) & 0xff;
+ }
+ }
+
+ /* Compress NH?
+ * Only if UDP for now. @todo support other NH compression. */
+ if (IP6H_NEXTH(ip6hdr) == IP6_NEXTH_UDP) {
+ buffer[ieee_header_len] |= 0x04;
+ } else {
+ /* append nexth. */
+ buffer[ieee_header_len + lowpan6_header_len++] = IP6H_NEXTH(ip6hdr);
+ }
+
+ /* Compress hop limit? */
+ if (IP6H_HOPLIM(ip6hdr) == 255) {
+ buffer[ieee_header_len] |= 0x03;
+ } else if (IP6H_HOPLIM(ip6hdr) == 64) {
+ buffer[ieee_header_len] |= 0x02;
+ } else if (IP6H_HOPLIM(ip6hdr) == 1) {
+ buffer[ieee_header_len] |= 0x01;
+ } else {
+ /* append hop limit */
+ buffer[ieee_header_len + lowpan6_header_len++] = IP6H_HOPLIM(ip6hdr);
+ }
+
+ /* Compress source address */
+ if (((buffer[ieee_header_len + 1] & 0x40) != 0) ||
+ (ip6_addr_islinklocal(ip_2_ip6(&ip_data.current_iphdr_src)))) {
+ /* Context-based or link-local source address compression. */
+ i = lowpan6_get_address_mode(ip_2_ip6(&ip_data.current_iphdr_src), src);
+ buffer[ieee_header_len + 1] |= (i & 0x03) << 4;
+ if (i == 1) {
+ MEMCPY(buffer + ieee_header_len + lowpan6_header_len, (u8_t*)p->payload + 16, 8);
+ lowpan6_header_len += 8;
+ } else if (i == 2) {
+ MEMCPY(buffer + ieee_header_len + lowpan6_header_len, (u8_t*)p->payload + 22, 2);
+ lowpan6_header_len += 2;
+ }
+ } else if (ip6_addr_isany(ip_2_ip6(&ip_data.current_iphdr_src))) {
+ /* Special case: mark SAC and leave SAM=0 */
+ buffer[ieee_header_len + 1] |= 0x40;
+ } else {
+ /* Append full address. */
+ MEMCPY(buffer + ieee_header_len + lowpan6_header_len, (u8_t*)p->payload + 8, 16);
+ lowpan6_header_len += 16;
+ }
+
+ /* Compress destination address */
+ if (ip6_addr_ismulticast(ip_2_ip6(&ip_data.current_iphdr_dest))) {
+ /* @todo support stateful multicast address compression */
+
+ buffer[ieee_header_len + 1] |= 0x08;
+
+ i = lowpan6_get_address_mode_mc(ip_2_ip6(&ip_data.current_iphdr_dest));
+ buffer[ieee_header_len + 1] |= i & 0x03;
+ if (i == 0) {
+ MEMCPY(buffer + ieee_header_len + lowpan6_header_len, (u8_t*)p->payload + 24, 16);
+ lowpan6_header_len += 16;
+ } else if (i == 1) {
+ buffer[ieee_header_len + lowpan6_header_len++] = ((u8_t *)p->payload)[25];
+ MEMCPY(buffer + ieee_header_len + lowpan6_header_len, (u8_t*)p->payload + 35, 5);
+ lowpan6_header_len += 5;
+ } else if (i == 2) {
+ buffer[ieee_header_len + lowpan6_header_len++] = ((u8_t *)p->payload)[25];
+ MEMCPY(buffer + ieee_header_len + lowpan6_header_len, (u8_t*)p->payload + 37, 3);
+ lowpan6_header_len += 3;
+ } else if (i == 3) {
+ buffer[ieee_header_len + lowpan6_header_len++] = ((u8_t *)p->payload)[39];
+ }
+ } else if (((buffer[ieee_header_len + 1] & 0x04) != 0) ||
+ (ip6_addr_islinklocal(ip_2_ip6(&ip_data.current_iphdr_dest)))) {
+ /* Context-based or link-local destination address compression. */
+ i = lowpan6_get_address_mode(ip_2_ip6(&ip_data.current_iphdr_dest), dst);
+ buffer[ieee_header_len + 1] |= i & 0x03;
+ if (i == 1) {
+ MEMCPY(buffer + ieee_header_len + lowpan6_header_len, (u8_t*)p->payload + 32, 8);
+ lowpan6_header_len += 8;
+ } else if (i == 2) {
+ MEMCPY(buffer + ieee_header_len + lowpan6_header_len, (u8_t*)p->payload + 38, 2);
+ lowpan6_header_len += 2;
+ }
+ } else {
+ /* Append full address. */
+ MEMCPY(buffer + ieee_header_len + lowpan6_header_len, (u8_t*)p->payload + 24, 16);
+ lowpan6_header_len += 16;
+ }
+
+ /* Move to payload. */
+ pbuf_header(p, -IP6_HLEN);
+
+ /* Compress UDP header? */
+ if (IP6H_NEXTH(ip6hdr) == IP6_NEXTH_UDP) {
+ /* @todo support optional checksum compression */
+
+ buffer[ieee_header_len + lowpan6_header_len] = 0xf0;
+
+ /* determine port compression mode. */
+ if ((((u8_t *)p->payload)[0] == 0xf0) && ((((u8_t *)p->payload)[1] & 0xf0) == 0xb0) &&
+ (((u8_t *)p->payload)[2] == 0xf0) && ((((u8_t *)p->payload)[3] & 0xf0) == 0xb0)) {
+ /* Compress source and dest ports. */
+ buffer[ieee_header_len + lowpan6_header_len++] |= 0x03;
+ buffer[ieee_header_len + lowpan6_header_len++] = ((((u8_t *)p->payload)[1] & 0x0f) << 4) | (((u8_t *)p->payload)[3] & 0x0f);
+ } else if (((u8_t *)p->payload)[0] == 0xf0) {
+ /* Compress source port. */
+ buffer[ieee_header_len + lowpan6_header_len++] |= 0x02;
+ buffer[ieee_header_len + lowpan6_header_len++] = ((u8_t *)p->payload)[1];
+ buffer[ieee_header_len + lowpan6_header_len++] = ((u8_t *)p->payload)[2];
+ buffer[ieee_header_len + lowpan6_header_len++] = ((u8_t *)p->payload)[3];
+ } else if (((u8_t *)p->payload)[2] == 0xf0) {
+ /* Compress dest port. */
+ buffer[ieee_header_len + lowpan6_header_len++] |= 0x01;
+ buffer[ieee_header_len + lowpan6_header_len++] = ((u8_t *)p->payload)[0];
+ buffer[ieee_header_len + lowpan6_header_len++] = ((u8_t *)p->payload)[1];
+ buffer[ieee_header_len + lowpan6_header_len++] = ((u8_t *)p->payload)[3];
+ } else {
+ /* append full ports. */
+ lowpan6_header_len++;
+ buffer[ieee_header_len + lowpan6_header_len++] = ((u8_t *)p->payload)[0];
+ buffer[ieee_header_len + lowpan6_header_len++] = ((u8_t *)p->payload)[1];
+ buffer[ieee_header_len + lowpan6_header_len++] = ((u8_t *)p->payload)[2];
+ buffer[ieee_header_len + lowpan6_header_len++] = ((u8_t *)p->payload)[3];
+ }
+
+ /* elide length and copy checksum */
+ buffer[ieee_header_len + lowpan6_header_len++] = ((u8_t *)p->payload)[6];
+ buffer[ieee_header_len + lowpan6_header_len++] = ((u8_t *)p->payload)[7];
+
+ pbuf_header(p, -UDP_HLEN);
+ }
+ }
+
+#else /* LWIP_6LOWPAN_HC */
+ /* Send uncompressed IPv6 header with appropriate dispatch byte. */
+ lowpan6_header_len = 1;
+ buffer[ieee_header_len] = 0x41; /* IPv6 dispatch */
+#endif /* LWIP_6LOWPAN_HC */
+
+ /* Calculate remaining packet length */
+ remaining_len = p->tot_len;
+
+ if (remaining_len > 0x7FF) {
+ MIB2_STATS_NETIF_INC(netif, ifoutdiscards);
+ /* datagram_size must fit into 11 bit */
+ pbuf_free(p_frag);
+ return ERR_VAL;
+ }
+
+ /* Fragment, or 1 packet? */
+ if (remaining_len > (127 - ieee_header_len - lowpan6_header_len - 3)) { /* 127 - header - 1 byte dispatch - 2 bytes CRC */
+ /* We must move the 6LowPAN header to make room for the FRAG header. */
+ i = lowpan6_header_len;
+ while (i-- != 0) {
+ buffer[ieee_header_len + i + 4] = buffer[ieee_header_len + i];
+ }
+
+ /* Now we need to fragment the packet. FRAG1 header first */
+ buffer[ieee_header_len] = 0xc0 | (((p->tot_len + lowpan6_header_len) >> 8) & 0x7);
+ buffer[ieee_header_len + 1] = (p->tot_len + lowpan6_header_len) & 0xff;
+
+ datagram_tag++;
+ buffer[ieee_header_len + 2] = datagram_tag & 0xff;
+ buffer[ieee_header_len + 3] = (datagram_tag >> 8) & 0xff;
+
+ /* Fragment follows. */
+ frag_len = (127 - ieee_header_len - 4 - 2) & 0xf8;
+
+ pbuf_copy_partial(p, buffer + ieee_header_len + lowpan6_header_len + 4, frag_len - lowpan6_header_len, 0);
+ remaining_len -= frag_len - lowpan6_header_len;
+ datagram_offset = frag_len;
+
+ /* 2 bytes CRC */
+#if LWIP_6LOWPAN_HW_CRC
+ /* Leave blank, will be filled by HW. */
+#else /* LWIP_6LOWPAN_HW_CRC */
+ /* @todo calculate CRC */
+#endif /* LWIP_6LOWPAN_HW_CRC */
+
+ /* Calculate frame length */
+ p_frag->len = p_frag->tot_len = ieee_header_len + 4 + frag_len + 2; /* add 2 dummy bytes for crc*/
+
+ /* send the packet */
+ MIB2_STATS_NETIF_ADD(netif, ifoutoctets, p_frag->tot_len);
+ LWIP_DEBUGF(LOWPAN6_DEBUG | LWIP_DBG_TRACE, ("lowpan6_send: sending packet %p\n", (void *)p));
+ err = netif->linkoutput(netif, p_frag);
+
+ while ((remaining_len > 0) && (err == ERR_OK)) {
+ /* new frame, new seq num for ACK */
+ buffer[2] = frame_seq_num++;
+
+ buffer[ieee_header_len] |= 0x20; /* Change FRAG1 to FRAGN */
+
+ buffer[ieee_header_len + 4] = (u8_t)(datagram_offset >> 3); /* datagram offset in FRAGN header (datagram_offset is max. 11 bit) */
+
+ frag_len = (127 - ieee_header_len - 5 - 2) & 0xf8;
+ if (frag_len > remaining_len) {
+ frag_len = remaining_len;
+ }
+
+ pbuf_copy_partial(p, buffer + ieee_header_len + 5, frag_len, p->tot_len - remaining_len);
+ remaining_len -= frag_len;
+ datagram_offset += frag_len;
+
+ /* 2 bytes CRC */
+#if LWIP_6LOWPAN_HW_CRC
+ /* Leave blank, will be filled by HW. */
+#else /* LWIP_6LOWPAN_HW_CRC */
+ /* @todo calculate CRC */
+#endif /* LWIP_6LOWPAN_HW_CRC */
+
+ /* Calculate frame length */
+ p_frag->len = p_frag->tot_len = frag_len + 5 + ieee_header_len + 2;
+
+ /* send the packet */
+ MIB2_STATS_NETIF_ADD(netif, ifoutoctets, p_frag->tot_len);
+ LWIP_DEBUGF(LOWPAN6_DEBUG | LWIP_DBG_TRACE, ("lowpan6_send: sending packet %p\n", (void *)p));
+ err = netif->linkoutput(netif, p_frag);
+ }
+ } else {
+ /* It fits in one frame. */
+ frag_len = remaining_len;
+
+ /* Copy IPv6 packet */
+ pbuf_copy_partial(p, buffer + ieee_header_len + lowpan6_header_len, frag_len, 0);
+ remaining_len = 0;
+
+ /* 2 bytes CRC */
+#if LWIP_6LOWPAN_HW_CRC
+ /* Leave blank, will be filled by HW. */
+#else /* LWIP_6LOWPAN_HW_CRC */
+ /* @todo calculate CRC */
+#endif /* LWIP_6LOWPAN_HW_CRC */
+
+ /* Calculate frame length */
+ p_frag->len = p_frag->tot_len = frag_len + lowpan6_header_len + ieee_header_len + 2;
+
+ /* send the packet */
+ MIB2_STATS_NETIF_ADD(netif, ifoutoctets, p_frag->tot_len);
+ LWIP_DEBUGF(LOWPAN6_DEBUG | LWIP_DBG_TRACE, ("lowpan6_send: sending packet %p\n", (void *)p));
+ err = netif->linkoutput(netif, p_frag);
+ }
+
+ pbuf_free(p_frag);
+
+ return err;
+}
+
+err_t
+lowpan6_set_context(u8_t idx, const ip6_addr_t * context)
+{
+ if (idx >= LWIP_6LOWPAN_NUM_CONTEXTS) {
+ return ERR_ARG;
+ }
+
+ ip6_addr_set(&lowpan6_context[idx], context);
+
+ return ERR_OK;
+}
+
+#if LWIP_6LOWPAN_INFER_SHORT_ADDRESS
+err_t
+lowpan6_set_short_addr(u8_t addr_high, u8_t addr_low)
+{
+ short_mac_addr.addr[0] = addr_high;
+ short_mac_addr.addr[1] = addr_low;
+
+ return ERR_OK;
+}
+#endif /* LWIP_6LOWPAN_INFER_SHORT_ADDRESS */
+
+#if LWIP_IPV4
+err_t
+lowpan4_output(struct netif *netif, struct pbuf *q, const ip4_addr_t *ipaddr)
+{
+ (void)netif;
+ (void)q;
+ (void)ipaddr;
+
+ return ERR_IF;
+}
+#endif /* LWIP_IPV4 */
+
+/**
+ * Resolve and fill-in IEEE 802.15.4 address header for outgoing IPv6 packet.
+ *
+ * Perform Header Compression and fragment if necessary.
+ *
+ * @param netif The lwIP network interface which the IP packet will be sent on.
+ * @param q The pbuf(s) containing the IP packet to be sent.
+ * @param ip6addr The IP address of the packet destination.
+ *
+ * @return err_t
+ */
+err_t
+lowpan6_output(struct netif *netif, struct pbuf *q, const ip6_addr_t *ip6addr)
+{
+ err_t result;
+ const u8_t *hwaddr;
+ struct ieee_802154_addr src, dest;
+#if LWIP_6LOWPAN_INFER_SHORT_ADDRESS
+ ip6_addr_t ip6_src;
+ struct ip6_hdr * ip6_hdr;
+#endif /* LWIP_6LOWPAN_INFER_SHORT_ADDRESS */
+
+#if LWIP_6LOWPAN_INFER_SHORT_ADDRESS
+ /* Check if we can compress source address (use aligned copy) */
+ ip6_hdr = (struct ip6_hdr *)q->payload;
+ ip6_addr_set(&ip6_src, &ip6_hdr->src);
+ if (lowpan6_get_address_mode(&ip6_src, &short_mac_addr) == 3) {
+ src.addr_len = 2;
+ src.addr[0] = short_mac_addr.addr[0];
+ src.addr[1] = short_mac_addr.addr[1];
+ } else
+#endif /* LWIP_6LOWPAN_INFER_SHORT_ADDRESS */
+ {
+ src.addr_len = netif->hwaddr_len;
+ SMEMCPY(src.addr, netif->hwaddr, netif->hwaddr_len);
+ }
+
+ /* multicast destination IP address? */
+ if (ip6_addr_ismulticast(ip6addr)) {
+ MIB2_STATS_NETIF_INC(netif, ifoutnucastpkts);
+ /* We need to send to the broadcast address.*/
+ return lowpan6_frag(netif, q, &src, &ieee_802154_broadcast);
+ }
+
+ /* We have a unicast destination IP address */
+ /* @todo anycast? */
+
+#if LWIP_6LOWPAN_INFER_SHORT_ADDRESS
+ if (src.addr_len == 2) {
+ /* If source address was compressable to short_mac_addr, and dest has same subnet and
+ * is also compressable to 2-bytes, assume we can infer dest as a short address too. */
+ dest.addr_len = 2;
+ dest.addr[0] = ((u8_t *)q->payload)[38];
+ dest.addr[1] = ((u8_t *)q->payload)[39];
+ if ((src.addr_len == 2) && (ip6_addr_netcmp(&ip6_hdr->src, &ip6_hdr->dest)) &&
+ (lowpan6_get_address_mode(ip6addr, &dest) == 3)) {
+ MIB2_STATS_NETIF_INC(netif, ifoutucastpkts);
+ return lowpan6_frag(netif, q, &src, &dest);
+ }
+ }
+#endif /* LWIP_6LOWPAN_INFER_SHORT_ADDRESS */
+
+ /* Ask ND6 what to do with the packet. */
+ result = nd6_get_next_hop_addr_or_queue(netif, q, ip6addr, &hwaddr);
+ if (result != ERR_OK) {
+ MIB2_STATS_NETIF_INC(netif, ifoutdiscards);
+ return result;
+ }
+
+ /* If no hardware address is returned, nd6 has queued the packet for later. */
+ if (hwaddr == NULL) {
+ return ERR_OK;
+ }
+
+ /* Send out the packet using the returned hardware address. */
+ dest.addr_len = netif->hwaddr_len;
+ SMEMCPY(dest.addr, hwaddr, netif->hwaddr_len);
+ MIB2_STATS_NETIF_INC(netif, ifoutucastpkts);
+ return lowpan6_frag(netif, q, &src, &dest);
+}
+
+static struct pbuf *
+lowpan6_decompress(struct pbuf * p, struct ieee_802154_addr * src, struct ieee_802154_addr * dest)
+{
+ struct pbuf * q;
+ u8_t * lowpan6_buffer;
+ s8_t lowpan6_offset;
+ struct ip6_hdr *ip6hdr;
+ s8_t i;
+ s8_t ip6_offset = IP6_HLEN;
+
+
+ q = pbuf_alloc(PBUF_IP, p->len + IP6_HLEN + UDP_HLEN, PBUF_POOL);
+ if (q == NULL) {
+ pbuf_free(p);
+ return NULL;
+ }
+
+ lowpan6_buffer = (u8_t *)p->payload;
+ ip6hdr = (struct ip6_hdr *)q->payload;
+
+ lowpan6_offset = 2;
+ if (lowpan6_buffer[1] & 0x80) {
+ lowpan6_offset++;
+ }
+
+ /* Set IPv6 version, traffic class and flow label. */
+ if ((lowpan6_buffer[0] & 0x18) == 0x00) {
+ IP6H_VTCFL_SET(ip6hdr, 6, lowpan6_buffer[lowpan6_offset], ((lowpan6_buffer[lowpan6_offset+1] & 0x0f) << 16) | (lowpan6_buffer[lowpan6_offset + 2] << 8) | lowpan6_buffer[lowpan6_offset+3]);
+ lowpan6_offset += 4;
+ } else if ((lowpan6_buffer[0] & 0x18) == 0x08) {
+ IP6H_VTCFL_SET(ip6hdr, 6, lowpan6_buffer[lowpan6_offset] & 0xc0, ((lowpan6_buffer[lowpan6_offset] & 0x0f) << 16) | (lowpan6_buffer[lowpan6_offset + 1] << 8) | lowpan6_buffer[lowpan6_offset+2]);
+ lowpan6_offset += 3;
+ } else if ((lowpan6_buffer[0] & 0x18) == 0x10) {
+ IP6H_VTCFL_SET(ip6hdr, 6, lowpan6_buffer[lowpan6_offset],0);
+ lowpan6_offset += 1;
+ } else if ((lowpan6_buffer[0] & 0x18) == 0x18) {
+ IP6H_VTCFL_SET(ip6hdr, 6, 0, 0);
+ }
+
+ /* Set Next Header */
+ if ((lowpan6_buffer[0] & 0x04) == 0x00) {
+ IP6H_NEXTH_SET(ip6hdr, lowpan6_buffer[lowpan6_offset++]);
+ } else {
+ /* We should fill this later with NHC decoding */
+ IP6H_NEXTH_SET(ip6hdr, 0);
+ }
+
+ /* Set Hop Limit */
+ if ((lowpan6_buffer[0] & 0x03) == 0x00) {
+ IP6H_HOPLIM_SET(ip6hdr, lowpan6_buffer[lowpan6_offset++]);
+ } else if ((lowpan6_buffer[0] & 0x03) == 0x01) {
+ IP6H_HOPLIM_SET(ip6hdr, 1);
+ } else if ((lowpan6_buffer[0] & 0x03) == 0x02) {
+ IP6H_HOPLIM_SET(ip6hdr, 64);
+ } else if ((lowpan6_buffer[0] & 0x03) == 0x03) {
+ IP6H_HOPLIM_SET(ip6hdr, 255);
+ }
+
+ /* Source address decoding. */
+ if ((lowpan6_buffer[1] & 0x40) == 0x00) {
+ /* Stateless compression */
+ if ((lowpan6_buffer[1] & 0x30) == 0x00) {
+ /* copy full address */
+ MEMCPY(&ip6hdr->src.addr[0], lowpan6_buffer + lowpan6_offset, 16);
+ lowpan6_offset += 16;
+ } else if ((lowpan6_buffer[1] & 0x30) == 0x10) {
+ ip6hdr->src.addr[0] = PP_HTONL(0xfe800000UL);
+ ip6hdr->src.addr[1] = 0;
+ MEMCPY(&ip6hdr->src.addr[2], lowpan6_buffer + lowpan6_offset, 8);
+ lowpan6_offset += 8;
+ } else if ((lowpan6_buffer[1] & 0x30) == 0x20) {
+ ip6hdr->src.addr[0] = PP_HTONL(0xfe800000UL);
+ ip6hdr->src.addr[1] = 0;
+ ip6hdr->src.addr[2] = PP_HTONL(0x000000ffUL);
+ ip6hdr->src.addr[3] = lwip_htonl(0xfe000000UL | (lowpan6_buffer[lowpan6_offset] << 8) |
+ lowpan6_buffer[lowpan6_offset+1]);
+ lowpan6_offset += 2;
+ } else if ((lowpan6_buffer[1] & 0x30) == 0x30) {
+ ip6hdr->src.addr[0] = PP_HTONL(0xfe800000UL);
+ ip6hdr->src.addr[1] = 0;
+ if (src->addr_len == 2) {
+ ip6hdr->src.addr[2] = PP_HTONL(0x000000ffUL);
+ ip6hdr->src.addr[3] = lwip_htonl(0xfe000000UL | (src->addr[0] << 8) | src->addr[1]);
+ } else {
+ ip6hdr->src.addr[2] = lwip_htonl(((src->addr[0] ^ 2) << 24) | (src->addr[1] << 16) |
+ (src->addr[2] << 8) | src->addr[3]);
+ ip6hdr->src.addr[3] = lwip_htonl((src->addr[4] << 24) | (src->addr[5] << 16) |
+ (src->addr[6] << 8) | src->addr[7]);
+ }
+ }
+ } else {
+ /* Stateful compression */
+ if ((lowpan6_buffer[1] & 0x30) == 0x00) {
+ /* ANY address */
+ ip6hdr->src.addr[0] = 0;
+ ip6hdr->src.addr[1] = 0;
+ ip6hdr->src.addr[2] = 0;
+ ip6hdr->src.addr[3] = 0;
+ } else {
+ /* Set prefix from context info */
+ if (lowpan6_buffer[1] & 0x80) {
+ i = (lowpan6_buffer[2] >> 4) & 0x0f;
+ } else {
+ i = 0;
+ }
+ if (i >= LWIP_6LOWPAN_NUM_CONTEXTS) {
+ /* Error */
+ pbuf_free(p);
+ pbuf_free(q);
+ return NULL;
+ }
+
+ ip6hdr->src.addr[0] = lowpan6_context[i].addr[0];
+ ip6hdr->src.addr[1] = lowpan6_context[i].addr[1];
+ }
+
+ if ((lowpan6_buffer[1] & 0x30) == 0x10) {
+ MEMCPY(&ip6hdr->src.addr[2], lowpan6_buffer + lowpan6_offset, 8);
+ lowpan6_offset += 8;
+ } else if ((lowpan6_buffer[1] & 0x30) == 0x20) {
+ ip6hdr->src.addr[2] = PP_HTONL(0x000000ffUL);
+ ip6hdr->src.addr[3] = lwip_htonl(0xfe000000UL | (lowpan6_buffer[lowpan6_offset] << 8) | lowpan6_buffer[lowpan6_offset+1]);
+ lowpan6_offset += 2;
+ } else if ((lowpan6_buffer[1] & 0x30) == 0x30) {
+ if (src->addr_len == 2) {
+ ip6hdr->src.addr[2] = PP_HTONL(0x000000ffUL);
+ ip6hdr->src.addr[3] = lwip_htonl(0xfe000000UL | (src->addr[0] << 8) | src->addr[1]);
+ } else {
+ ip6hdr->src.addr[2] = lwip_htonl(((src->addr[0] ^ 2) << 24) | (src->addr[1] << 16) | (src->addr[2] << 8) | src->addr[3]);
+ ip6hdr->src.addr[3] = lwip_htonl((src->addr[4] << 24) | (src->addr[5] << 16) | (src->addr[6] << 8) | src->addr[7]);
+ }
+ }
+ }
+
+ /* Destination address decoding. */
+ if (lowpan6_buffer[1] & 0x08) {
+ /* Multicast destination */
+ if (lowpan6_buffer[1] & 0x04) {
+ /* @todo support stateful multicast addressing */
+ pbuf_free(p);
+ pbuf_free(q);
+ return NULL;
+ }
+
+ if ((lowpan6_buffer[1] & 0x03) == 0x00) {
+ /* copy full address */
+ MEMCPY(&ip6hdr->dest.addr[0], lowpan6_buffer + lowpan6_offset, 16);
+ lowpan6_offset += 16;
+ } else if ((lowpan6_buffer[1] & 0x03) == 0x01) {
+ ip6hdr->dest.addr[0] = lwip_htonl(0xff000000UL | (lowpan6_buffer[lowpan6_offset++] << 16));
+ ip6hdr->dest.addr[1] = 0;
+ ip6hdr->dest.addr[2] = lwip_htonl(lowpan6_buffer[lowpan6_offset++]);
+ ip6hdr->dest.addr[3] = lwip_htonl((lowpan6_buffer[lowpan6_offset] << 24) | (lowpan6_buffer[lowpan6_offset + 1] << 16) | (lowpan6_buffer[lowpan6_offset + 2] << 8) | lowpan6_buffer[lowpan6_offset + 3]);
+ lowpan6_offset += 4;
+ } else if ((lowpan6_buffer[1] & 0x03) == 0x02) {
+ ip6hdr->dest.addr[0] = lwip_htonl(0xff000000UL | lowpan6_buffer[lowpan6_offset++]);
+ ip6hdr->dest.addr[1] = 0;
+ ip6hdr->dest.addr[2] = 0;
+ ip6hdr->dest.addr[3] = lwip_htonl((lowpan6_buffer[lowpan6_offset] << 16) | (lowpan6_buffer[lowpan6_offset + 1] << 8) | lowpan6_buffer[lowpan6_offset + 2]);
+ lowpan6_offset += 3;
+ } else if ((lowpan6_buffer[1] & 0x03) == 0x03) {
+ ip6hdr->dest.addr[0] = PP_HTONL(0xff020000UL);
+ ip6hdr->dest.addr[1] = 0;
+ ip6hdr->dest.addr[2] = 0;
+ ip6hdr->dest.addr[3] = lwip_htonl(lowpan6_buffer[lowpan6_offset++]);
+ }
+
+ } else {
+ if (lowpan6_buffer[1] & 0x04) {
+ /* Stateful destination compression */
+ /* Set prefix from context info */
+ if (lowpan6_buffer[1] & 0x80) {
+ i = lowpan6_buffer[2] & 0x0f;
+ } else {
+ i = 0;
+ }
+ if (i >= LWIP_6LOWPAN_NUM_CONTEXTS) {
+ /* Error */
+ pbuf_free(p);
+ pbuf_free(q);
+ return NULL;
+ }
+
+ ip6hdr->dest.addr[0] = lowpan6_context[i].addr[0];
+ ip6hdr->dest.addr[1] = lowpan6_context[i].addr[1];
+ } else {
+ /* Link local address compression */
+ ip6hdr->dest.addr[0] = PP_HTONL(0xfe800000UL);
+ ip6hdr->dest.addr[1] = 0;
+ }
+
+ if ((lowpan6_buffer[1] & 0x03) == 0x00) {
+ /* copy full address */
+ MEMCPY(&ip6hdr->dest.addr[0], lowpan6_buffer + lowpan6_offset, 16);
+ lowpan6_offset += 16;
+ } else if ((lowpan6_buffer[1] & 0x03) == 0x01) {
+ MEMCPY(&ip6hdr->dest.addr[2], lowpan6_buffer + lowpan6_offset, 8);
+ lowpan6_offset += 8;
+ } else if ((lowpan6_buffer[1] & 0x03) == 0x02) {
+ ip6hdr->dest.addr[2] = PP_HTONL(0x000000ffUL);
+ ip6hdr->dest.addr[3] = lwip_htonl(0xfe000000UL | (lowpan6_buffer[lowpan6_offset] << 8) | lowpan6_buffer[lowpan6_offset + 1]);
+ lowpan6_offset += 2;
+ } else if ((lowpan6_buffer[1] & 0x03) == 0x03) {
+ if (dest->addr_len == 2) {
+ ip6hdr->dest.addr[2] = PP_HTONL(0x000000ffUL);
+ ip6hdr->dest.addr[3] = lwip_htonl(0xfe000000UL | (dest->addr[0] << 8) | dest->addr[1]);
+ } else {
+ ip6hdr->dest.addr[2] = lwip_htonl(((dest->addr[0] ^ 2) << 24) | (dest->addr[1] << 16) | dest->addr[2] << 8 | dest->addr[3]);
+ ip6hdr->dest.addr[3] = lwip_htonl((dest->addr[4] << 24) | (dest->addr[5] << 16) | dest->addr[6] << 8 | dest->addr[7]);
+ }
+ }
+ }
+
+
+ /* Next Header Compression (NHC) decoding? */
+ if (lowpan6_buffer[0] & 0x04) {
+ if ((lowpan6_buffer[lowpan6_offset] & 0xf8) == 0xf0) {
+ struct udp_hdr *udphdr;
+
+ /* UDP compression */
+ IP6H_NEXTH_SET(ip6hdr, IP6_NEXTH_UDP);
+ udphdr = (struct udp_hdr *)((u8_t *)q->payload + ip6_offset);
+
+ if (lowpan6_buffer[lowpan6_offset] & 0x04) {
+ /* @todo support checksum decompress */
+ pbuf_free(p);
+ pbuf_free(q);
+ return NULL;
+ }
+
+ /* Decompress ports */
+ i = lowpan6_buffer[lowpan6_offset++] & 0x03;
+ if (i == 0) {
+ udphdr->src = lwip_htons(lowpan6_buffer[lowpan6_offset] << 8 | lowpan6_buffer[lowpan6_offset + 1]);
+ udphdr->dest = lwip_htons(lowpan6_buffer[lowpan6_offset + 2] << 8 | lowpan6_buffer[lowpan6_offset + 3]);
+ lowpan6_offset += 4;
+ } else if (i == 0x01) {
+ udphdr->src = lwip_htons(lowpan6_buffer[lowpan6_offset] << 8 | lowpan6_buffer[lowpan6_offset + 1]);
+ udphdr->dest = lwip_htons(0xf000 | lowpan6_buffer[lowpan6_offset + 2]);
+ lowpan6_offset += 3;
+ } else if (i == 0x02) {
+ udphdr->src = lwip_htons(0xf000 | lowpan6_buffer[lowpan6_offset]);
+ udphdr->dest = lwip_htons(lowpan6_buffer[lowpan6_offset + 1] << 8 | lowpan6_buffer[lowpan6_offset + 2]);
+ lowpan6_offset += 3;
+ } else if (i == 0x03) {
+ udphdr->src = lwip_htons(0xf0b0 | ((lowpan6_buffer[lowpan6_offset] >> 4) & 0x0f));
+ udphdr->dest = lwip_htons(0xf0b0 | (lowpan6_buffer[lowpan6_offset] & 0x0f));
+ lowpan6_offset += 1;
+ }
+
+ udphdr->chksum = lwip_htons(lowpan6_buffer[lowpan6_offset] << 8 | lowpan6_buffer[lowpan6_offset + 1]);
+ lowpan6_offset += 2;
+ udphdr->len = lwip_htons(p->tot_len - lowpan6_offset + UDP_HLEN);
+
+ ip6_offset += UDP_HLEN;
+ } else {
+ /* @todo support NHC other than UDP */
+ pbuf_free(p);
+ pbuf_free(q);
+ return NULL;
+ }
+ }
+
+ /* Now we copy leftover contents from p to q, so we have all L2 and L3 headers (and L4?) in a single PBUF.
+ * Replace p with q, and free p */
+ pbuf_header(p, -lowpan6_offset);
+ MEMCPY((u8_t*)q->payload + ip6_offset, p->payload, p->len);
+ q->len = q->tot_len = ip6_offset + p->len;
+ if (p->next != NULL) {
+ pbuf_cat(q, p->next);
+ }
+ p->next = NULL;
+ pbuf_free(p);
+
+ /* Infer IPv6 payload length for header */
+ IP6H_PLEN_SET(ip6hdr, q->tot_len - IP6_HLEN);
+
+ /* all done */
+ return q;
+}
+
+err_t
+lowpan6_input(struct pbuf * p, struct netif *netif)
+{
+ u8_t * puc;
+ s8_t i;
+ struct ieee_802154_addr src, dest;
+ u16_t datagram_size, datagram_offset, datagram_tag;
+ struct lowpan6_reass_helper *lrh, *lrh_temp;
+
+ MIB2_STATS_NETIF_ADD(netif, ifinoctets, p->tot_len);
+
+ /* Analyze header. @todo validate. */
+ puc = (u8_t*)p->payload;
+ datagram_offset = 5;
+ if ((puc[1] & 0x0c) == 0x0c) {
+ dest.addr_len = 8;
+ for (i = 0; i < 8; i++) {
+ dest.addr[i] = puc[datagram_offset + 7 - i];
+ }
+ datagram_offset += 8;
+ } else {
+ dest.addr_len = 2;
+ dest.addr[0] = puc[datagram_offset + 1];
+ dest.addr[1] = puc[datagram_offset];
+ datagram_offset += 2;
+ }
+
+ datagram_offset += 2; /* skip PAN ID. */
+
+ if ((puc[1] & 0xc0) == 0xc0) {
+ src.addr_len = 8;
+ for (i = 0; i < 8; i++) {
+ src.addr[i] = puc[datagram_offset + 7 - i];
+ }
+ datagram_offset += 8;
+ } else {
+ src.addr_len = 2;
+ src.addr[0] = puc[datagram_offset + 1];
+ src.addr[1] = puc[datagram_offset];
+ datagram_offset += 2;
+ }
+
+ pbuf_header(p, -datagram_offset); /* hide IEEE802.15.4 header. */
+
+ /* Check dispatch. */
+ puc = (u8_t*)p->payload;
+
+ if ((*puc & 0xf8) == 0xc0) {
+ /* FRAG1 dispatch. add this packet to reassembly list. */
+ datagram_size = ((u16_t)(puc[0] & 0x07) << 8) | (u16_t)puc[1];
+ datagram_tag = ((u16_t)puc[2] << 8) | (u16_t)puc[3];
+
+ /* check for duplicate */
+ lrh = reass_list;
+ while (lrh != NULL) {
+ if ((lrh->sender_addr.addr_len == src.addr_len) &&
+ (memcmp(lrh->sender_addr.addr, src.addr, src.addr_len) == 0)) {
+ /* address match with packet in reassembly. */
+ if ((datagram_tag == lrh->datagram_tag) && (datagram_size == lrh->datagram_size)) {
+ MIB2_STATS_NETIF_INC(netif, ifindiscards);
+ /* duplicate fragment. */
+ pbuf_free(p);
+ return ERR_OK;
+ } else {
+ /* We are receiving the start of a new datagram. Discard old one (incomplete). */
+ lrh_temp = lrh->next_packet;
+ dequeue_datagram(lrh);
+ pbuf_free(lrh->pbuf);
+ mem_free(lrh);
+
+ /* Check next datagram in queue. */
+ lrh = lrh_temp;
+ }
+ } else {
+ /* Check next datagram in queue. */
+ lrh = lrh->next_packet;
+ }
+ }
+
+ pbuf_header(p, -4); /* hide frag1 dispatch */
+
+ lrh = (struct lowpan6_reass_helper *) mem_malloc(sizeof(struct lowpan6_reass_helper));
+ if (lrh == NULL) {
+ MIB2_STATS_NETIF_INC(netif, ifindiscards);
+ pbuf_free(p);
+ return ERR_MEM;
+ }
+
+ lrh->sender_addr.addr_len = src.addr_len;
+ for (i = 0; i < src.addr_len; i++) {
+ lrh->sender_addr.addr[i] = src.addr[i];
+ }
+ lrh->datagram_size = datagram_size;
+ lrh->datagram_tag = datagram_tag;
+ lrh->pbuf = p;
+ lrh->next_packet = reass_list;
+ lrh->timer = 2;
+ reass_list = lrh;
+
+ return ERR_OK;
+ } else if ((*puc & 0xf8) == 0xe0) {
+ /* FRAGN dispatch, find packet being reassembled. */
+ datagram_size = ((u16_t)(puc[0] & 0x07) << 8) | (u16_t)puc[1];
+ datagram_tag = ((u16_t)puc[2] << 8) | (u16_t)puc[3];
+ datagram_offset = (u16_t)puc[4] << 3;
+ pbuf_header(p, -5); /* hide frag1 dispatch */
+
+ for (lrh = reass_list; lrh != NULL; lrh = lrh->next_packet) {
+ if ((lrh->sender_addr.addr_len == src.addr_len) &&
+ (memcmp(lrh->sender_addr.addr, src.addr, src.addr_len) == 0) &&
+ (datagram_tag == lrh->datagram_tag) &&
+ (datagram_size == lrh->datagram_size)) {
+ break;
+ }
+ }
+ if (lrh == NULL) {
+ /* rogue fragment */
+ MIB2_STATS_NETIF_INC(netif, ifindiscards);
+ pbuf_free(p);
+ return ERR_OK;
+ }
+
+ if (lrh->pbuf->tot_len < datagram_offset) {
+ /* duplicate, ignore. */
+ pbuf_free(p);
+ return ERR_OK;
+ } else if (lrh->pbuf->tot_len > datagram_offset) {
+ MIB2_STATS_NETIF_INC(netif, ifindiscards);
+ /* We have missed a fragment. Delete whole reassembly. */
+ dequeue_datagram(lrh);
+ pbuf_free(lrh->pbuf);
+ mem_free(lrh);
+ pbuf_free(p);
+ return ERR_OK;
+ }
+ pbuf_cat(lrh->pbuf, p);
+ p = NULL;
+
+ /* is packet now complete?*/
+ if (lrh->pbuf->tot_len >= lrh->datagram_size) {
+ /* dequeue from reass list. */
+ dequeue_datagram(lrh);
+
+ /* get pbuf */
+ p = lrh->pbuf;
+
+ /* release helper */
+ mem_free(lrh);
+ } else {
+ return ERR_OK;
+ }
+ }
+
+ if (p == NULL) {
+ return ERR_OK;
+ }
+
+ /* We have a complete packet, check dispatch for headers. */
+ puc = (u8_t*)p->payload;
+
+ if (*puc == 0x41) {
+ /* This is a complete IPv6 packet, just skip dispatch byte. */
+ pbuf_header(p, -1); /* hide dispatch byte. */
+ } else if ((*puc & 0xe0 )== 0x60) {
+ /* IPv6 headers are compressed using IPHC. */
+ p = lowpan6_decompress(p, &src, &dest);
+ if (p == NULL) {
+ MIB2_STATS_NETIF_INC(netif, ifindiscards);
+ return ERR_OK;
+ }
+ } else {
+ MIB2_STATS_NETIF_INC(netif, ifindiscards);
+ pbuf_free(p);
+ return ERR_OK;
+ }
+
+ /* @todo: distinguish unicast/multicast */
+ MIB2_STATS_NETIF_INC(netif, ifinucastpkts);
+
+ return ip6_input(p, netif);
+}
+
+err_t
+lowpan6_if_init(struct netif *netif)
+{
+ netif->name[0] = 'L';
+ netif->name[1] = '6';
+#if LWIP_IPV4
+ netif->output = lowpan4_output;
+#endif /* LWIP_IPV4 */
+ netif->output_ip6 = lowpan6_output;
+
+ MIB2_INIT_NETIF(netif, snmp_ifType_other, 0);
+
+ /* maximum transfer unit */
+ netif->mtu = 1280;
+
+ /* broadcast capability */
+ netif->flags = NETIF_FLAG_BROADCAST /* | NETIF_FLAG_LOWPAN6 */;
+
+ return ERR_OK;
+}
+
+err_t
+lowpan6_set_pan_id(u16_t pan_id)
+{
+ ieee_802154_pan_id = pan_id;
+
+ return ERR_OK;
+}
+
+#if !NO_SYS
+/**
+ * Pass a received packet to tcpip_thread for input processing
+ *
+ * @param p the received packet, p->payload pointing to the
+ * IEEE 802.15.4 header.
+ * @param inp the network interface on which the packet was received
+ */
+err_t
+tcpip_6lowpan_input(struct pbuf *p, struct netif *inp)
+{
+ return tcpip_inpkt(p, inp, lowpan6_input);
+}
+#endif /* !NO_SYS */
+
+#endif /* LWIP_IPV6 && LWIP_6LOWPAN */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/PPPD_FOLLOWUP b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/PPPD_FOLLOWUP
new file mode 100644
index 0000000..c231982
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/PPPD_FOLLOWUP
@@ -0,0 +1,473 @@
+The lwIP PPP support is based from pppd 2.4.5 (http://ppp.samba.org) with
+huge changes to match code size and memory requirements for embedded devices.
+
+Anyway, pppd has a mature codebase for years and the average commit count
+is getting low on their Git repository, meaning that we can follow what
+is happening on their side and merge what is relevant for lwIP.
+
+So, here is the pppd follow up, so that we don't get away too far from pppd.
+
+
+== Patch fetched from from pppd Debian packages ==
+
+This has nothing to do with pppd, but we merged some good patch from
+Debian and this is a good place to be.
+
+- LCP adaptive echo, so that we don't send LCP echo request if we
+ are receiving data from peer, can be enabled by setting PPP_LCP_ADAPTIVE
+ to true.
+
+- IPCP no/replace default route option, were added in the early stage of
+ the ppp port, but it wasn't really helpful and was disabled when adding
+ the new API ppp_set_default() call, which gives the lwIP user control over
+ which one is the default interface, it was actually a requirement if you
+ are doing PPP over PPP (i.e. PPPoL2TP, VPN link, over PPPoE, ADSL link).
+
+- using rp-pppoe pppd exits with EXIT_OK after receiving a timeout waiting
+ for PADO due to no modem attached, bug reported to pppd bug tracker, fixed
+ in Debian but not in the latest (at the time when the port were started)
+ pppd release.
+
+
+== Commits on pppd ==
+
+2010-03-06 - Document +ipv6 and ipv6cp-accept-local
+ e7537958aee79b3f653c601e903cb31d78fb7dcc
+
+Don't care.
+
+
+2010-03-06 - Install pppol2tp plugins with sane permissions
+ 406215672cfadc03017341fe03802d1c7294b903
+
+Don't care.
+
+
+2010-03-07 - pppd: Terminate correctly if lcp_lowerup delayed calling
+ fsm_lowerup
+ 3eb9e810cfa515543655659b72dde30c54fea0a5
+
+Merged 2012-05-17.
+
+
+2010-03-07 - rp_pppoe: Copy acName and pppd_pppoe_service after option parsing
+ cab58617fd9d328029fffabc788020264b4fa91f
+
+Don't care, is a patch for pppd/plugins/rp-pppoe/plugin.c which is not part
+of the port.
+
+
+2010-08-23 - set and reset options to control environment variables
+ for scripts.
+ 2b6310fd24dba8e0fca8999916a162f0a1842a84
+
+We can't fork processes in embedded, therefore all the pppd process run
+feature is disabled in the port, so we don't care about the new
+"environment variables" pppd feature.
+
+
+2010-08-23 - Nit: use _exit when exec fails and restrict values to 0-255
+ per POSIX.
+ 2b4ea140432eeba5a007c0d4e6236bd0e0c12ba4
+
+Again, we are not running as a heavy process, so all exit() or _exit() calls
+were removed.
+
+
+2010-08-23 - Fix quote handling in configuration files to be more like shell
+ quoting.
+ 3089132cdf5b58dbdfc2daf08ec5c08eb47f8aca
+
+We are not parsing config file, all the filesystem I/O stuff were disabled
+in our port.
+
+
+2010-08-24 - rp-pppoe: allow MTU to be increased up to 1500
+ fd1dcdf758418f040da3ed801ab001b5e46854e7
+
+Only concern changes on RP-PPPoE plugin, which we don't use.
+
+
+2010-09-11 - chat: Allow TIMEOUT value to come from environment variable
+ ae80bf833e48a6202f44a935a68083ae52ad3824
+
+See 2b6310fd24dba8e0fca8999916a162f0a1842a84.
+
+
+2011-03-05 - pppdump: Fix printfs with insufficient arguments
+ 7b8db569642c83ba3283745034f2e2c95e459423
+
+pppdump is a ppp tool outside pppd source tree.
+
+
+2012-05-06 - pppd: Don't unconditionally disable VJ compression under Linux
+ d8a66adf98a0e525cf38031b42098d539da6eeb6
+
+Patch for sys-linux.c, which we don't use.
+
+
+2012-05-20 - Remove old version of Linux if_pppol2tp.h
+ c41092dd4c49267f232f6cba3d31c6c68bfdf68d
+
+Not in the port.
+
+
+2012-05-20 - pppd: Make MSCHAP-v2 cope better with packet loss
+ 08ef47ca532294eb428238c831616748940e24a2
+
+This is an interesting patch. However it consumes much more memory for
+MSCHAP and I am not sure if the benefit worth it. The PPP client can
+always start the authentication again if it failed for whatever reason.
+
+
+2012-05-20 - scripts: Make poff ignore extra arguments to pppd
+ 18f515f32c9f5723a9c2c912601e04335106534b
+
+Again, we are not running scripts.
+
+
+2012-05-20 - rp-pppoe plugin: Print leading zeros in MAC address
+ f5dda0cfc220c4b52e26144096d729e27b30f0f7
+
+Again, we are not using the RP-PPPoE plugin.
+
+
+2012-05-20 - pppd: Notify IPv6 up/down as we do for IPv4
+ 845cda8fa18939cf56e60b073f63a7efa65336fc
+
+This is just a patch that adds plugins hooks for IPv6, the plugin interface
+was disabled because we don't have .so plugins in embedded.
+
+
+2012-05-20 - pppd: Enable IPV6 by default and fix some warnings
+ 0b6118239615e98959f7e0b4e746bdd197533248
+
+Change on Makefile for IPv6, warnings were already cleared during port.
+
+
+2012-05-20 - contrib: Fix pppgetpass.gtk compilation
+ 80a8e2ce257ca12cce723519a0f20ea1d663b14a
+
+Change on Makefile, don't care.
+
+
+2012-05-20 - pppd: Don't crash if crypt() returns NULL
+ 04c4348108d847e034dd91066cc6843f60d71731
+
+We are using the PolarSSL DES implementation that does not return NULL.
+
+
+2012-05-20 - pppd: Eliminate some warnings
+ c44ae5e6a7338c96eb463881fe709b2dfaffe568
+
+Again, we are handling compilation warnings on our own.
+
+
+2012-05-20 - rp-pppoe plugin: Import some fixes from rp-pppoe-3.10
+ 1817d83e51a411044e730ba89ebdb0480e1c8cd4
+
+Once more, we are not using the RP-PPPoE plugin.
+
+
+2013-01-23 - pppd: Clarify circumstances where DNS1/DNS2 environment variables are set
+ cf2f5c9538b9400ade23446a194729b0a4113b3a
+
+Documentation only.
+
+
+2013-02-03 - ppp: ignore unrecognised radiusclient configuration directives
+ 7f736dde0da3c19855997d9e67370e351e15e923
+
+Radius plugin, not in the port.
+
+
+2013-02-03 - pppd: Take out unused %r conversion completely
+ 356d8d558d844412119aa18c8e5a113bc6459c7b
+
+Merged 2014-04-15.
+
+
+2013-02-03 - pppd: Arrange to use logwtmp from libutil on Linux
+ 9617a7eb137f4fee62799a677a9ecf8d834db3f5
+
+Patch for sys-linux.c, which we don't use.
+
+
+2013-02-03 - pppdump: Eliminate some compiler warnings
+ 3e3acf1ba2b3046c072a42c19164788a9e419bd1
+
+pppdump is a ppp tool outside pppd source tree.
+
+
+2013-02-03 - chat: Correct spelling errors in the man page
+ 8dea1b969d266ccbf6f3a8c5474eb6dcd8838e3b
+
+Documentation only.
+
+
+2013-02-03 - pppd: Fix spelling errors in man page
+ 9e05a25d76b3f83096c661678010320df673df6b
+
+Documentation only.
+
+
+2013-02-03 - plugins/passprompt: Fix potential out-of-bounds array reference
+ 8edb889b753056a691a3e4b217a110a35f9fdedb
+
+Plugin patch, we do not have plugins.
+
+
+2013-02-03 - chat: Fix *roff errors in the man page
+ a7c3489eeaf44e83ce592143c7c8a5b5c29f4c48
+
+Documentation only.
+
+
+2013-03-02 - pppd: Fix man page description of case when remote IP address isn't known
+ 224841f4799f4f1e2e71bc490c54448d66740f4f
+
+Documentation only.
+
+
+2013-03-02 - pppd: Add master_detach option
+ 398ed2585640d198c53e736ee5bbd67f7ce8168e
+
+Option for multilink support, we do not support multilink and this option
+is about detaching from the terminal, which is out of the embedded scope.
+
+
+2013-03-11 - pppd: Default exit status to EXIT_CONNECT_FAILED during connection phase
+ 225361d64ae737afdc8cb57579a2f33525461bc9
+
+Commented out in our port, and already fixed by a previously applied Debian patch.
+
+
+2013-03-11 - pppstats: Fix undefined macro in man page
+ d16a3985eade5280b8e171f5dd0670a91cba0d39
+
+Documentation only.
+
+
+2013-05-11 - plugins/radius: Handle bindaddr keyword in radiusclient.conf
+ d883b2dbafeed3ebd9d7a56ab1469373bd001a3b
+
+Radius plugin, not in the port.
+
+
+2013-06-09 - pppoatm: Remove explicit loading of pppoatm kernel module
+ 52cd43a84bea524033b918b603698104f221bbb7
+
+PPPoATM plugin, not in the port.
+
+
+2013-06-09 - pppd: Fix segfault in update_db_entry()
+ 37476164f15a45015310b9d4b197c2d7db1f7f8f
+
+We do not use the samba db.
+
+
+2013-06-09 - chat: Fix some text that was intended to be literal
+ cd9683676618adcee8add2c3cfa3382341b5a1f6
+
+Documentation only.
+
+
+2013-06-09 - README.pppoe: Minor semantic fix
+ b5b8898af6fd3d44e873cfc66810ace5f1f47e17
+
+Documentation only.
+
+
+2013-06-10 - radius: Handle additional attributes
+ 2f581cd986a56f2ec4a95abad4f8297a1b10d7e2
+
+Radius plugin, not in the port.
+
+
+2013-06-10 - chat, pppd: Use \e instead of \\ in man pages
+ 8d6942415d22f6ca4377340ca26e345c3f5fa5db
+
+Documentation only.
+
+
+2014-01-02 - pppd: Don't crash if NULL pointer passed to vslprintf for %q or %v
+ 906814431bddeb2061825fa1ebad1a967b6d87a9
+
+Merged 2014-04-15.
+
+
+2014-01-02 - pppd: Accept IPCP ConfAck packets containing MS-WINS options
+ a243f217f1c6ac1aa7793806bc88590d077f490a
+
+Merged 2014-04-15.
+
+
+2014-01-02 - config: Update Solaris compiler options and enable CHAPMS and IPV6
+ 99c46caaed01b7edba87962aa52b77fad61bfd7b
+
+Solaris port, don't care.
+
+
+2014-01-02 - Update README and patchlevel for 2.4.6 release
+ 4043750fca36e7e0eb90d702e048ad1da4929418
+
+Just release stuff.
+
+
+2014-02-18 - pppd: Add option "stop-bits" to set number of serial port stop bits.
+ ad993a20ee485f0d0e2ac4105221641b200da6e2
+
+Low level serial port, not in the port.
+
+
+2014-03-09 - pppd: Separate IPv6 handling for sifup/sifdown
+ b04d2dc6df5c6b5650fea44250d58757ee3dac4a
+
+Reimplemented.
+
+
+2014-03-09 - pppol2tp: Connect up/down events to notifiers and add IPv6 ones
+ fafbe50251efc7d6b4a8be652d085316e112b34f
+
+Not in the port.
+
+
+2014-03-09 - pppd: Add declarations to eliminate compile warnings
+ 50967962addebe15c7a7e63116ff46a0441dc464
+
+We are handling compilation warnings on our own
+
+
+2014-03-09 - pppd: Eliminate some unnecessary ifdefs
+ de8da14d845ee6db9236ccfddabf1d8ebf045ddb
+
+We mostly did that previously. Anyway, merged 2014-12-24.
+
+
+2014-08-01 - radius: Fix realms-config-file option
+ 880a81be7c8e0fe8567227bc17a1bff3ea035943
+
+Radius plugin, not in the port.
+
+
+2014-08-01 - pppd: Eliminate potential integer overflow in option parsing
+ 7658e8257183f062dc01f87969c140707c7e52cb
+
+pppd config file parser, not in the port.
+
+
+2014-08-01 - pppd: Eliminate memory leak with multiple instances of a string option
+ b94b7fbbaa0589aa6ec5fdc733aeb9ff294d2656
+
+pppd config file parser, not in the port.
+
+
+2014-08-01 - pppd: Fix a stack variable overflow in MSCHAP-v2
+ 36733a891fb56594fcee580f667b33a64b990981
+
+This fixes a bug introduced in 08ef47ca ("pppd: Make MSCHAP-v2 cope better with packet loss").
+
+We didn't merge 08ef47ca ;-)
+
+
+2014-08-01 - winbind plugin: Add -DMPPE=1 to eliminate compiler warnings
+ 2b05e22c62095e97dd0a97e4b5588402c2185071
+
+Linux plugin, not in the port.
+
+
+2014-08-09 - Update README and patchlevel for 2.4.7 release
+ 6e8eaa7a78b31cdab2edf140a9c8afdb02ffaca5
+
+Just release stuff.
+
+
+2014-08-10 - abort on errors in subdir builds
+ 5e90783d11a59268e05f4cfb29ce2343b13e8ab2
+
+Linux Makefile, not in the port.
+
+
+2014-06-03 - pppd: add support for defaultroute-metric option
+ 35e5a569c988b1ff865b02a24d9a727a00db4da9
+
+Only necessary for Linux, lwIP does not support route metrics.
+
+
+2014-12-13 - scripts: Avoid killing wrong pppd
+ 67811a647d399db5d188a242827760615a0f86b5
+
+pppd helper script, not in the port.
+
+
+2014-12-20 - pppd: Fix sign-extension when displaying bytes in octal
+ 5e8c3cb256a7e86e3572a82a75d51c6850efdbdc
+
+Merged 2016-07-02.
+
+
+2015-03-01 - Suppress false error message on PPPoE disconnect
+ 219aac3b53d0827549377f1bfe22853ee52d4405
+
+PPPoE plugin, not in the port.
+
+
+2015-03-01 - Send PADT on PPPoE disconnect
+ cd2c14f998c57bbe6a01dc5854f2763c0d7f31fb
+
+PPPoE plugin, not in the port. And our PPPoE implementation already does
+that: pppoe_disconnect() calls pppoe_send_padt().
+
+
+2015-08-14 - pppd: ipxcp: Prevent buffer overrun on remote router name
+ fe149de624f96629a7f46732055d8f718c74b856
+
+We never ported IPX support. lwIP does not support IPX.
+
+
+2015-03-25 - pppd: Fix ccp_options.mppe type
+ 234edab99a6bb250cc9ecd384cca27b0c8b475ce
+
+We found that while working on MPPE support in lwIP, that's our patch ;-)
+
+
+2015-03-24 - pppd: Fix ccp_cilen calculated size if both deflate_correct and deflate_draft are enabled
+ 094cb8ae4c61db225e67fedadb4964f846dd0c27
+
+We found that while working on MPPE support in lwIP, that's our patch ;-)
+
+
+2015-08-14 - Merge branch 'master' of https://github.com/ncopa/ppp
+ 3a5c9a8fbc8970375cd881151d44e4b6fe249c6a
+
+Merge commit, we don't care.
+
+
+2015-08-14 - Merge branch 'master' of git://github.com/vapier/ppp
+ 912e4fc6665aca188dced7ea7fdc663ce5a2dd24
+
+Merge commit, we don't care.
+
+
+2015-08-14 - Merge branch 'bug_fix' of git://github.com/radaiming/ppp
+ dfd33d7f526ecd7b39dd1bba8101260d02af5ebb
+
+Merge commit, we don't care.
+
+
+2015-08-14 - Merge branch 'master' of git://github.com/pprindeville/ppp
+ aa4a985f6114d08cf4e47634fb6325da71016473
+
+Merge commit, we don't care.
+
+
+2015-08-14 - Merge branch 'no-error-on-already-closed' of git://github.com/farnz/ppp
+ 6edf252483b30dbcdcc5059f01831455365d5b6e
+
+Merge commit, we don't care.
+
+
+2015-08-14 - Merge branch 'send-padt-on-disconnect' of git://github.com/farnz/ppp
+ 84684243d651f55f6df69d2a6707b52fbbe62bb9
+
+Merge commit, we don't care.
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/auth.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/auth.c
new file mode 100644
index 0000000..c8673ad
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/auth.c
@@ -0,0 +1,2510 @@
+/*
+ * auth.c - PPP authentication and phase control.
+ *
+ * Copyright (c) 1993-2002 Paul Mackerras. 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. The name(s) of the authors of this software must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission.
+ *
+ * 3. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by Paul Mackerras
+ * <paulus@samba.org>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Derived from main.c, which is:
+ *
+ * Copyright (c) 1984-2000 Carnegie Mellon University. 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 "Carnegie Mellon University" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission. For permission or any legal
+ * details, please contact
+ * Office of Technology Transfer
+ * Carnegie Mellon University
+ * 5000 Forbes Avenue
+ * Pittsburgh, PA 15213-3890
+ * (412) 268-4387, fax: (412) 268-7395
+ * tech-transfer@andrew.cmu.edu
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by Computing Services
+ * at Carnegie Mellon University (http://www.cmu.edu/computing/)."
+ *
+ * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
+ * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#if 0 /* UNUSED */
+#include <stdio.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <pwd.h>
+#include <grp.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <utmp.h>
+#include <fcntl.h>
+#if defined(_PATH_LASTLOG) && defined(__linux__)
+#include <lastlog.h>
+#endif
+
+#include <netdb.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#ifdef HAS_SHADOW
+#include <shadow.h>
+#ifndef PW_PPP
+#define PW_PPP PW_LOGIN
+#endif
+#endif
+
+#include <time.h>
+#endif /* UNUSED */
+
+#include "netif/ppp/ppp_impl.h"
+
+#include "netif/ppp/fsm.h"
+#include "netif/ppp/lcp.h"
+#if CCP_SUPPORT
+#include "netif/ppp/ccp.h"
+#endif /* CCP_SUPPORT */
+#if ECP_SUPPORT
+#include "netif/ppp/ecp.h"
+#endif /* ECP_SUPPORT */
+#include "netif/ppp/ipcp.h"
+#if PAP_SUPPORT
+#include "netif/ppp/upap.h"
+#endif /* PAP_SUPPORT */
+#if CHAP_SUPPORT
+#include "netif/ppp/chap-new.h"
+#endif /* CHAP_SUPPORT */
+#if EAP_SUPPORT
+#include "netif/ppp/eap.h"
+#endif /* EAP_SUPPORT */
+#if CBCP_SUPPORT
+#include "netif/ppp/cbcp.h"
+#endif
+
+#if 0 /* UNUSED */
+#include "session.h"
+#endif /* UNUSED */
+
+#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 0 /* UNUSED */
+/* List of addresses which the peer may use. */
+static struct permitted_ip *addresses[NUM_PPP];
+
+/* Wordlist giving addresses which the peer may use
+ without authenticating itself. */
+static struct wordlist *noauth_addrs;
+
+/* Remote telephone number, if available */
+char remote_number[MAXNAMELEN];
+
+/* Wordlist giving remote telephone numbers which may connect. */
+static struct wordlist *permitted_numbers;
+
+/* Extra options to apply, from the secrets file entry for the peer. */
+static struct wordlist *extra_options;
+#endif /* UNUSED */
+
+#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) (struct ppp_idle *) = NULL;
+
+/* Hook for a plugin to say whether we can possibly authenticate any peer */
+int (*pap_check_hook) (void) = NULL;
+
+/* Hook for a plugin to check the PAP user and password */
+int (*pap_auth_hook) (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) (void) = NULL;
+
+/* Hook for a plugin to get the PAP password for authenticating us */
+int (*pap_passwd_hook) (char *user, char *passwd) = NULL;
+
+/* Hook for a plugin to say if we can possibly authenticate a peer using CHAP */
+int (*chap_check_hook) (void) = NULL;
+
+/* Hook for a plugin to get the CHAP password for authenticating us */
+int (*chap_passwd_hook) (char *user, char *passwd) = NULL;
+
+/* Hook for a plugin to say whether it is OK if the peer
+ refuses to authenticate. */
+int (*null_auth_hook) (struct wordlist **paddrs,
+ struct wordlist **popts) = NULL;
+
+int (*allowed_address_hook) (u32_t addr) = NULL;
+#endif /* UNUSED */
+
+#ifdef HAVE_MULTILINK
+/* Hook for plugin to hear when an interface joins a multilink bundle */
+void (*multilink_join_hook) (void) = NULL;
+#endif
+
+#if PPP_NOTIFY
+/* A notifier for when the peer has authenticated itself,
+ and we are proceeding to the network phase. */
+struct notifier *auth_up_notifier = NULL;
+
+/* A notifier for when the link goes down. */
+struct notifier *link_down_notifier = NULL;
+#endif /* PPP_NOTIFY */
+
+/*
+ * Option variables.
+ */
+#if 0 /* MOVED TO ppp_settings */
+bool uselogin = 0; /* Use /etc/passwd for checking PAP */
+bool session_mgmt = 0; /* Do session management (login records) */
+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 refuse_eap = 0; /* Don't wanna auth. ourselves with EAP */
+#if MSCHAP_SUPPORT
+bool refuse_mschap = 0; /* Don't wanna auth. ourselves with MS-CHAP */
+bool refuse_mschap_v2 = 0; /* Don't wanna auth. ourselves with MS-CHAPv2 */
+#else /* MSCHAP_SUPPORT */
+bool refuse_mschap = 1; /* Don't wanna auth. ourselves with MS-CHAP */
+bool refuse_mschap_v2 = 1; /* Don't wanna auth. ourselves with MS-CHAPv2 */
+#endif /* MSCHAP_SUPPORT */
+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 */
+bool explicit_user = 0; /* Set if "user" option supplied */
+bool explicit_passwd = 0; /* Set if "password" option supplied */
+char remote_name[MAXNAMELEN]; /* Peer's name for authentication */
+static char *uafname; /* name of most recent +ua file */
+
+extern char *crypt (const char *, const char *);
+#endif /* UNUSED */
+/* Prototypes for procedures local to this file. */
+
+static void network_phase(ppp_pcb *pcb);
+#if PPP_IDLETIMELIMIT
+static void check_idle(void *arg);
+#endif /* PPP_IDLETIMELIMIT */
+#if PPP_MAXCONNECT
+static void connect_time_expired(void *arg);
+#endif /* PPP_MAXCONNECT */
+#if 0 /* UNUSED */
+static int null_login (int);
+/* static int get_pap_passwd (char *); */
+static int have_pap_secret (int *);
+static int have_chap_secret (char *, char *, int, int *);
+static int have_srp_secret (char *client, char *server, int need_ip,
+ int *lacks_ipp);
+static int ip_addr_check (u32_t, struct permitted_ip *);
+static int scan_authfile (FILE *, char *, char *, char *,
+ struct wordlist **, struct wordlist **,
+ char *, int);
+static void free_wordlist (struct wordlist *);
+static void set_allowed_addrs (int, struct wordlist *, struct wordlist *);
+static int some_ip_ok (struct wordlist *);
+static int setupapfile (char **);
+static int privgroup (char **);
+static int set_noauth_addr (char **);
+static int set_permitted_number (char **);
+static void check_access (FILE *, char *);
+static int wordlist_count (struct wordlist *);
+#endif /* UNUSED */
+
+#ifdef MAXOCTETS
+static void check_maxoctets (void *);
+#endif
+
+#if PPP_OPTIONS
+/*
+ * Authentication-related options.
+ */
+option_t auth_options[] = {
+ { "auth", o_bool, &auth_required,
+ "Require authentication from peer", OPT_PRIO | 1 },
+ { "noauth", o_bool, &auth_required,
+ "Don't require peer to authenticate", OPT_PRIOSUB | OPT_PRIV,
+ &allow_any_ip },
+ { "require-pap", o_bool, &lcp_wantoptions[0].neg_upap,
+ "Require PAP authentication from peer",
+ OPT_PRIOSUB | 1, &auth_required },
+ { "+pap", o_bool, &lcp_wantoptions[0].neg_upap,
+ "Require PAP authentication from peer",
+ OPT_ALIAS | OPT_PRIOSUB | 1, &auth_required },
+ { "require-chap", o_bool, &auth_required,
+ "Require CHAP authentication from peer",
+ OPT_PRIOSUB | OPT_A2OR | MDTYPE_MD5,
+ &lcp_wantoptions[0].chap_mdtype },
+ { "+chap", o_bool, &auth_required,
+ "Require CHAP authentication from peer",
+ OPT_ALIAS | OPT_PRIOSUB | OPT_A2OR | MDTYPE_MD5,
+ &lcp_wantoptions[0].chap_mdtype },
+#if MSCHAP_SUPPORT
+ { "require-mschap", o_bool, &auth_required,
+ "Require MS-CHAP authentication from peer",
+ OPT_PRIOSUB | OPT_A2OR | MDTYPE_MICROSOFT,
+ &lcp_wantoptions[0].chap_mdtype },
+ { "+mschap", o_bool, &auth_required,
+ "Require MS-CHAP authentication from peer",
+ OPT_ALIAS | OPT_PRIOSUB | OPT_A2OR | MDTYPE_MICROSOFT,
+ &lcp_wantoptions[0].chap_mdtype },
+ { "require-mschap-v2", o_bool, &auth_required,
+ "Require MS-CHAPv2 authentication from peer",
+ OPT_PRIOSUB | OPT_A2OR | MDTYPE_MICROSOFT_V2,
+ &lcp_wantoptions[0].chap_mdtype },
+ { "+mschap-v2", o_bool, &auth_required,
+ "Require MS-CHAPv2 authentication from peer",
+ OPT_ALIAS | OPT_PRIOSUB | OPT_A2OR | MDTYPE_MICROSOFT_V2,
+ &lcp_wantoptions[0].chap_mdtype },
+#endif /* MSCHAP_SUPPORT */
+#if 0
+ { "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", OPT_ALIAS | 1 },
+ { "refuse-chap", o_bool, &refuse_chap,
+ "Don't agree to auth to peer with CHAP",
+ OPT_A2CLRB | MDTYPE_MD5,
+ &lcp_allowoptions[0].chap_mdtype },
+ { "-chap", o_bool, &refuse_chap,
+ "Don't allow CHAP authentication with peer",
+ OPT_ALIAS | OPT_A2CLRB | MDTYPE_MD5,
+ &lcp_allowoptions[0].chap_mdtype },
+#endif
+#if MSCHAP_SUPPORT
+#if 0
+ { "refuse-mschap", o_bool, &refuse_mschap,
+ "Don't agree to auth to peer with MS-CHAP",
+ OPT_A2CLRB | MDTYPE_MICROSOFT,
+ &lcp_allowoptions[0].chap_mdtype },
+ { "-mschap", o_bool, &refuse_mschap,
+ "Don't allow MS-CHAP authentication with peer",
+ OPT_ALIAS | OPT_A2CLRB | MDTYPE_MICROSOFT,
+ &lcp_allowoptions[0].chap_mdtype },
+ { "refuse-mschap-v2", o_bool, &refuse_mschap_v2,
+ "Don't agree to auth to peer with MS-CHAPv2",
+ OPT_A2CLRB | MDTYPE_MICROSOFT_V2,
+ &lcp_allowoptions[0].chap_mdtype },
+ { "-mschap-v2", o_bool, &refuse_mschap_v2,
+ "Don't allow MS-CHAPv2 authentication with peer",
+ OPT_ALIAS | OPT_A2CLRB | MDTYPE_MICROSOFT_V2,
+ &lcp_allowoptions[0].chap_mdtype },
+#endif
+#endif /* MSCHAP_SUPPORT*/
+#if EAP_SUPPORT
+ { "require-eap", o_bool, &lcp_wantoptions[0].neg_eap,
+ "Require EAP authentication from peer", OPT_PRIOSUB | 1,
+ &auth_required },
+#if 0
+ { "refuse-eap", o_bool, &refuse_eap,
+ "Don't agree to authenticate to peer with EAP", 1 },
+#endif
+#endif /* EAP_SUPPORT */
+ { "name", o_string, our_name,
+ "Set local name for authentication",
+ OPT_PRIO | OPT_PRIV | OPT_STATIC, NULL, MAXNAMELEN },
+
+ { "+ua", o_special, (void *)setupapfile,
+ "Get PAP user and password from file",
+ OPT_PRIO | OPT_A2STRVAL, &uafname },
+
+#if 0
+ { "user", o_string, user,
+ "Set name for auth with peer", OPT_PRIO | OPT_STATIC,
+ &explicit_user, MAXNAMELEN },
+
+ { "password", o_string, passwd,
+ "Password for authenticating us to the peer",
+ OPT_PRIO | OPT_STATIC | OPT_HIDE,
+ &explicit_passwd, MAXSECRETLEN },
+#endif
+
+ { "usehostname", o_bool, &usehostname,
+ "Must use hostname for authentication", 1 },
+
+ { "remotename", o_string, remote_name,
+ "Set remote name for authentication", OPT_PRIO | OPT_STATIC,
+ &explicit_remote, MAXNAMELEN },
+
+ { "login", o_bool, &uselogin,
+ "Use system password database for PAP", OPT_A2COPY | 1 ,
+ &session_mgmt },
+ { "enable-session", o_bool, &session_mgmt,
+ "Enable session accounting for remote peers", OPT_PRIV | 1 },
+
+ { "papcrypt", o_bool, &cryptpap,
+ "PAP passwords are encrypted", 1 },
+
+ { "privgroup", o_special, (void *)privgroup,
+ "Allow group members to use privileged options", OPT_PRIV | OPT_A2LIST },
+
+ { "allow-ip", o_special, (void *)set_noauth_addr,
+ "Set IP address(es) which can be used without authentication",
+ OPT_PRIV | OPT_A2LIST },
+
+ { "remotenumber", o_string, remote_number,
+ "Set remote telephone number for authentication", OPT_PRIO | OPT_STATIC,
+ NULL, MAXNAMELEN },
+
+ { "allow-number", o_special, (void *)set_permitted_number,
+ "Set telephone number(s) which are allowed to connect",
+ OPT_PRIV | OPT_A2LIST },
+
+ { NULL }
+};
+#endif /* PPP_OPTIONS */
+
+#if 0 /* UNUSED */
+/*
+ * setupapfile - specifies UPAP info for authenticating with peer.
+ */
+static int
+setupapfile(argv)
+ char **argv;
+{
+ FILE *ufile;
+ int l;
+ uid_t euid;
+ char u[MAXNAMELEN], p[MAXSECRETLEN];
+ char *fname;
+
+ lcp_allowoptions[0].neg_upap = 1;
+
+ /* open user info file */
+ fname = strdup(*argv);
+ if (fname == NULL)
+ novm("+ua file name");
+ euid = geteuid();
+ if (seteuid(getuid()) == -1) {
+ option_error("unable to reset uid before opening %s: %m", fname);
+ return 0;
+ }
+ ufile = fopen(fname, "r");
+ if (seteuid(euid) == -1)
+ fatal("unable to regain privileges: %m");
+ if (ufile == NULL) {
+ option_error("unable to open user login data file %s", fname);
+ return 0;
+ }
+ check_access(ufile, fname);
+ uafname = fname;
+
+ /* get username */
+ if (fgets(u, MAXNAMELEN - 1, ufile) == NULL
+ || fgets(p, MAXSECRETLEN - 1, ufile) == NULL) {
+ fclose(ufile);
+ option_error("unable to read user login data file %s", fname);
+ return 0;
+ }
+ fclose(ufile);
+
+ /* get rid of newlines */
+ l = strlen(u);
+ if (l > 0 && u[l-1] == '\n')
+ u[l-1] = 0;
+ l = strlen(p);
+ if (l > 0 && p[l-1] == '\n')
+ p[l-1] = 0;
+
+ if (override_value("user", option_priority, fname)) {
+ strlcpy(ppp_settings.user, u, sizeof(ppp_settings.user));
+ explicit_user = 1;
+ }
+ if (override_value("passwd", option_priority, fname)) {
+ strlcpy(ppp_settings.passwd, p, sizeof(ppp_settings.passwd));
+ explicit_passwd = 1;
+ }
+
+ return (1);
+}
+
+/*
+ * privgroup - allow members of the group to have privileged access.
+ */
+static int
+privgroup(argv)
+ 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;
+}
+
+
+/*
+ * 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(argv)
+ char **argv;
+{
+ char *addr = *argv;
+ int l = strlen(addr) + 1;
+ struct wordlist *wp;
+
+ wp = (struct wordlist *) malloc(sizeof(struct wordlist) + l);
+ if (wp == NULL)
+ novm("allow-ip argument");
+ wp->word = (char *) (wp + 1);
+ wp->next = noauth_addrs;
+ MEMCPY(wp->word, addr, l);
+ noauth_addrs = wp;
+ return 1;
+}
+
+
+/*
+ * set_permitted_number - set remote telephone number(s) that may connect.
+ */
+static int
+set_permitted_number(argv)
+ char **argv;
+{
+ char *number = *argv;
+ int l = strlen(number) + 1;
+ struct wordlist *wp;
+
+ wp = (struct wordlist *) malloc(sizeof(struct wordlist) + l);
+ if (wp == NULL)
+ novm("allow-number argument");
+ wp->word = (char *) (wp + 1);
+ wp->next = permitted_numbers;
+ MEMCPY(wp->word, number, l);
+ permitted_numbers = wp;
+ return 1;
+}
+#endif
+
+/*
+ * An Open on LCP has requested a change from Dead to Establish phase.
+ */
+void link_required(ppp_pcb *pcb) {
+ LWIP_UNUSED_ARG(pcb);
+}
+
+#if 0
+/*
+ * Bring the link up to the point of being able to do ppp.
+ */
+void start_link(unit)
+ int unit;
+{
+ ppp_pcb *pcb = &ppp_pcb_list[unit];
+ char *msg;
+
+ status = EXIT_NEGOTIATION_FAILED;
+ new_phase(pcb, PPP_PHASE_SERIALCONN);
+
+ hungup = 0;
+ devfd = the_channel->connect();
+ msg = "Connect script failed";
+ if (devfd < 0)
+ goto fail;
+
+ /* set up the serial device as a ppp interface */
+ /*
+ * N.B. we used to do tdb_writelock/tdb_writeunlock around this
+ * (from establish_ppp to set_ifunit). However, we won't be
+ * doing the set_ifunit in multilink mode, which is the only time
+ * we need the atomicity that the tdb_writelock/tdb_writeunlock
+ * gives us. Thus we don't need the tdb_writelock/tdb_writeunlock.
+ */
+ fd_ppp = the_channel->establish_ppp(devfd);
+ msg = "ppp establishment failed";
+ if (fd_ppp < 0) {
+ status = EXIT_FATAL_ERROR;
+ goto disconnect;
+ }
+
+ if (!demand && ifunit >= 0)
+ set_ifunit(1);
+
+ /*
+ * Start opening the connection and wait for
+ * incoming events (reply, timeout, etc.).
+ */
+ if (ifunit >= 0)
+ ppp_notice("Connect: %s <--> %s", ifname, ppp_devnam);
+ else
+ ppp_notice("Starting negotiation on %s", ppp_devnam);
+ add_fd(fd_ppp);
+
+ new_phase(pcb, PPP_PHASE_ESTABLISH);
+
+ lcp_lowerup(pcb);
+ return;
+
+ disconnect:
+ new_phase(pcb, PPP_PHASE_DISCONNECT);
+ if (the_channel->disconnect)
+ the_channel->disconnect();
+
+ fail:
+ new_phase(pcb, PPP_PHASE_DEAD);
+ if (the_channel->cleanup)
+ (*the_channel->cleanup)();
+}
+#endif
+
+/*
+ * LCP has terminated the link; go to the Dead phase and take the
+ * physical layer down.
+ */
+void link_terminated(ppp_pcb *pcb) {
+ if (pcb->phase == PPP_PHASE_DEAD
+#ifdef HAVE_MULTILINK
+ || pcb->phase == PPP_PHASE_MASTER
+#endif /* HAVE_MULTILINK */
+ )
+ return;
+ new_phase(pcb, PPP_PHASE_DISCONNECT);
+
+#if 0 /* UNUSED */
+ if (pap_logout_hook) {
+ pap_logout_hook();
+ }
+ session_end(devnam);
+#endif /* UNUSED */
+
+ if (!doing_multilink) {
+ ppp_notice("Connection terminated.");
+#if PPP_STATS_SUPPORT
+ print_link_stats();
+#endif /* PPP_STATS_SUPPORT */
+ } else
+ ppp_notice("Link terminated.");
+
+ lcp_lowerdown(pcb);
+
+ ppp_link_terminated(pcb);
+#if 0
+ /*
+ * Delete pid files before disestablishing ppp. Otherwise it
+ * can happen that another pppd gets the same unit and then
+ * we delete its pid file.
+ */
+ if (!doing_multilink && !demand)
+ remove_pidfiles();
+
+ /*
+ * If we may want to bring the link up again, transfer
+ * the ppp unit back to the loopback. Set the
+ * real serial device back to its normal mode of operation.
+ */
+ if (fd_ppp >= 0) {
+ remove_fd(fd_ppp);
+ clean_check();
+ the_channel->disestablish_ppp(devfd);
+ if (doing_multilink)
+ mp_exit_bundle();
+ fd_ppp = -1;
+ }
+ if (!hungup)
+ lcp_lowerdown(pcb);
+ if (!doing_multilink && !demand)
+ script_unsetenv("IFNAME");
+
+ /*
+ * Run disconnector script, if requested.
+ * XXX we may not be able to do this if the line has hung up!
+ */
+ if (devfd >= 0 && the_channel->disconnect) {
+ the_channel->disconnect();
+ devfd = -1;
+ }
+ if (the_channel->cleanup)
+ (*the_channel->cleanup)();
+
+ if (doing_multilink && multilink_master) {
+ if (!bundle_terminating)
+ new_phase(pcb, PPP_PHASE_MASTER);
+ else
+ mp_bundle_terminated();
+ } else
+ new_phase(pcb, PPP_PHASE_DEAD);
+#endif
+}
+
+/*
+ * LCP has gone down; it will either die or try to re-establish.
+ */
+void link_down(ppp_pcb *pcb) {
+#if PPP_NOTIFY
+ notify(link_down_notifier, 0);
+#endif /* PPP_NOTIFY */
+
+ if (!doing_multilink) {
+ upper_layers_down(pcb);
+ if (pcb->phase != PPP_PHASE_DEAD
+#ifdef HAVE_MULTILINK
+ && pcb->phase != PPP_PHASE_MASTER
+#endif /* HAVE_MULTILINK */
+ )
+ new_phase(pcb, PPP_PHASE_ESTABLISH);
+ }
+ /* XXX if doing_multilink, should do something to stop
+ network-layer traffic on the link */
+}
+
+void upper_layers_down(ppp_pcb *pcb) {
+ int i;
+ const struct protent *protp;
+
+ for (i = 0; (protp = protocols[i]) != NULL; ++i) {
+ if (protp->protocol != PPP_LCP && protp->lowerdown != NULL)
+ (*protp->lowerdown)(pcb);
+ if (protp->protocol < 0xC000 && protp->close != NULL)
+ (*protp->close)(pcb, "LCP down");
+ }
+ pcb->num_np_open = 0;
+ pcb->num_np_up = 0;
+}
+
+/*
+ * The link is established.
+ * Proceed to the Dead, Authenticate or Network phase as appropriate.
+ */
+void link_established(ppp_pcb *pcb) {
+#if PPP_AUTH_SUPPORT
+ int auth;
+#if PPP_SERVER
+#if PAP_SUPPORT
+ lcp_options *wo = &pcb->lcp_wantoptions;
+#endif /* PAP_SUPPORT */
+ lcp_options *go = &pcb->lcp_gotoptions;
+#endif /* PPP_SERVER */
+ lcp_options *ho = &pcb->lcp_hisoptions;
+#endif /* PPP_AUTH_SUPPORT */
+ int i;
+ const struct protent *protp;
+
+ /*
+ * Tell higher-level protocols that LCP is up.
+ */
+ if (!doing_multilink) {
+ for (i = 0; (protp = protocols[i]) != NULL; ++i)
+ if (protp->protocol != PPP_LCP
+ && protp->lowerup != NULL)
+ (*protp->lowerup)(pcb);
+ }
+
+#if PPP_AUTH_SUPPORT
+#if PPP_SERVER
+#if PPP_ALLOWED_ADDRS
+ if (!auth_required && noauth_addrs != NULL)
+ set_allowed_addrs(unit, NULL, NULL);
+#endif /* PPP_ALLOWED_ADDRS */
+
+ if (pcb->settings.auth_required && !(0
+#if PAP_SUPPORT
+ || go->neg_upap
+#endif /* PAP_SUPPORT */
+#if CHAP_SUPPORT
+ || go->neg_chap
+#endif /* CHAP_SUPPORT */
+#if EAP_SUPPORT
+ || go->neg_eap
+#endif /* EAP_SUPPORT */
+ )) {
+
+#if PPP_ALLOWED_ADDRS
+ /*
+ * We wanted the peer to authenticate itself, and it refused:
+ * if we have some address(es) it can use without auth, fine,
+ * otherwise 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 (noauth_addrs != NULL) {
+ set_allowed_addrs(unit, NULL, NULL);
+ } else
+#endif /* PPP_ALLOWED_ADDRS */
+ if (!pcb->settings.null_login
+#if PAP_SUPPORT
+ || !wo->neg_upap
+#endif /* PAP_SUPPORT */
+ ) {
+ ppp_warn("peer refused to authenticate: terminating link");
+#if 0 /* UNUSED */
+ status = EXIT_PEER_AUTH_FAILED;
+#endif /* UNUSED */
+ pcb->err_code = PPPERR_AUTHFAIL;
+ lcp_close(pcb, "peer refused to authenticate");
+ return;
+ }
+ }
+#endif /* PPP_SERVER */
+
+ new_phase(pcb, PPP_PHASE_AUTHENTICATE);
+ auth = 0;
+#if PPP_SERVER
+#if EAP_SUPPORT
+ if (go->neg_eap) {
+ eap_authpeer(pcb, PPP_OUR_NAME);
+ auth |= EAP_PEER;
+ } else
+#endif /* EAP_SUPPORT */
+#if CHAP_SUPPORT
+ if (go->neg_chap) {
+ chap_auth_peer(pcb, PPP_OUR_NAME, CHAP_DIGEST(go->chap_mdtype));
+ auth |= CHAP_PEER;
+ } else
+#endif /* CHAP_SUPPORT */
+#if PAP_SUPPORT
+ if (go->neg_upap) {
+ upap_authpeer(pcb);
+ auth |= PAP_PEER;
+ } else
+#endif /* PAP_SUPPORT */
+ {}
+#endif /* PPP_SERVER */
+
+#if EAP_SUPPORT
+ if (ho->neg_eap) {
+ eap_authwithpeer(pcb, pcb->settings.user);
+ auth |= EAP_WITHPEER;
+ } else
+#endif /* EAP_SUPPORT */
+#if CHAP_SUPPORT
+ if (ho->neg_chap) {
+ chap_auth_with_peer(pcb, pcb->settings.user, CHAP_DIGEST(ho->chap_mdtype));
+ auth |= CHAP_WITHPEER;
+ } else
+#endif /* CHAP_SUPPORT */
+#if PAP_SUPPORT
+ if (ho->neg_upap) {
+ upap_authwithpeer(pcb, pcb->settings.user, pcb->settings.passwd);
+ auth |= PAP_WITHPEER;
+ } else
+#endif /* PAP_SUPPORT */
+ {}
+
+ pcb->auth_pending = auth;
+ pcb->auth_done = 0;
+
+ if (!auth)
+#endif /* PPP_AUTH_SUPPORT */
+ network_phase(pcb);
+}
+
+/*
+ * Proceed to the network phase.
+ */
+static void network_phase(ppp_pcb *pcb) {
+#if CBCP_SUPPORT
+ ppp_pcb *pcb = &ppp_pcb_list[unit];
+#endif
+#if 0 /* UNUSED */
+ lcp_options *go = &lcp_gotoptions[unit];
+#endif /* UNUSED */
+
+#if 0 /* UNUSED */
+ /* Log calling number. */
+ if (*remote_number)
+ ppp_notice("peer from calling number %q authorized", remote_number);
+#endif /* UNUSED */
+
+#if PPP_NOTIFY
+ /*
+ * If the peer had to authenticate, notify it now.
+ */
+ if (0
+#if CHAP_SUPPORT
+ || go->neg_chap
+#endif /* CHAP_SUPPORT */
+#if PAP_SUPPORT
+ || go->neg_upap
+#endif /* PAP_SUPPORT */
+#if EAP_SUPPORT
+ || go->neg_eap
+#endif /* EAP_SUPPORT */
+ ) {
+ notify(auth_up_notifier, 0);
+ }
+#endif /* PPP_NOTIFY */
+
+#if CBCP_SUPPORT
+ /*
+ * If we negotiated callback, do it now.
+ */
+ if (go->neg_cbcp) {
+ new_phase(pcb, PPP_PHASE_CALLBACK);
+ (*cbcp_protent.open)(pcb);
+ return;
+ }
+#endif
+
+#if PPP_OPTIONS
+ /*
+ * Process extra options from the secrets file
+ */
+ if (extra_options) {
+ options_from_list(extra_options, 1);
+ free_wordlist(extra_options);
+ extra_options = 0;
+ }
+#endif /* PPP_OPTIONS */
+ start_networks(pcb);
+}
+
+void start_networks(ppp_pcb *pcb) {
+#if CCP_SUPPORT || ECP_SUPPORT
+ int i;
+ const struct protent *protp;
+#endif /* CCP_SUPPORT || ECP_SUPPORT */
+
+ new_phase(pcb, PPP_PHASE_NETWORK);
+
+#ifdef HAVE_MULTILINK
+ if (multilink) {
+ if (mp_join_bundle()) {
+ if (multilink_join_hook)
+ (*multilink_join_hook)();
+ if (updetach && !nodetach)
+ detach();
+ return;
+ }
+ }
+#endif /* HAVE_MULTILINK */
+
+#ifdef PPP_FILTER
+ if (!demand)
+ set_filters(&pass_filter, &active_filter);
+#endif
+#if CCP_SUPPORT || ECP_SUPPORT
+ /* Start CCP and ECP */
+ for (i = 0; (protp = protocols[i]) != NULL; ++i)
+ if (
+ (0
+#if ECP_SUPPORT
+ || protp->protocol == PPP_ECP
+#endif /* ECP_SUPPORT */
+#if CCP_SUPPORT
+ || protp->protocol == PPP_CCP
+#endif /* CCP_SUPPORT */
+ )
+ && protp->open != NULL)
+ (*protp->open)(pcb);
+#endif /* CCP_SUPPORT || ECP_SUPPORT */
+
+ /*
+ * Bring up other network protocols iff encryption is not required.
+ */
+ if (1
+#if ECP_SUPPORT
+ && !ecp_gotoptions[unit].required
+#endif /* ECP_SUPPORT */
+#if MPPE_SUPPORT
+ && !pcb->ccp_gotoptions.mppe
+#endif /* MPPE_SUPPORT */
+ )
+ continue_networks(pcb);
+}
+
+void continue_networks(ppp_pcb *pcb) {
+ int i;
+ const struct protent *protp;
+
+ /*
+ * Start the "real" network protocols.
+ */
+ for (i = 0; (protp = protocols[i]) != NULL; ++i)
+ if (protp->protocol < 0xC000
+#if CCP_SUPPORT
+ && protp->protocol != PPP_CCP
+#endif /* CCP_SUPPORT */
+#if ECP_SUPPORT
+ && protp->protocol != PPP_ECP
+#endif /* ECP_SUPPORT */
+ && protp->open != NULL) {
+ (*protp->open)(pcb);
+ ++pcb->num_np_open;
+ }
+
+ if (pcb->num_np_open == 0)
+ /* nothing to do */
+ lcp_close(pcb, "No network protocols running");
+}
+
+#if PPP_AUTH_SUPPORT
+#if PPP_SERVER
+/*
+ * auth_check_passwd - Check the user name and passwd against configuration.
+ *
+ * returns:
+ * 0: Authentication failed.
+ * 1: Authentication succeeded.
+ * In either case, msg points to an appropriate message and msglen to the message len.
+ */
+int auth_check_passwd(ppp_pcb *pcb, char *auser, int userlen, char *apasswd, int passwdlen, const char **msg, int *msglen) {
+ int secretuserlen;
+ int secretpasswdlen;
+
+ if (pcb->settings.user && pcb->settings.passwd) {
+ secretuserlen = (int)strlen(pcb->settings.user);
+ secretpasswdlen = (int)strlen(pcb->settings.passwd);
+ if (secretuserlen == userlen
+ && secretpasswdlen == passwdlen
+ && !memcmp(auser, pcb->settings.user, userlen)
+ && !memcmp(apasswd, pcb->settings.passwd, passwdlen) ) {
+ *msg = "Login ok";
+ *msglen = sizeof("Login ok")-1;
+ return 1;
+ }
+ }
+
+ *msg = "Login incorrect";
+ *msglen = sizeof("Login incorrect")-1;
+ return 0;
+}
+
+/*
+ * The peer has failed to authenticate himself using `protocol'.
+ */
+void auth_peer_fail(ppp_pcb *pcb, int protocol) {
+ LWIP_UNUSED_ARG(protocol);
+ /*
+ * Authentication failure: take the link down
+ */
+#if 0 /* UNUSED */
+ status = EXIT_PEER_AUTH_FAILED;
+#endif /* UNUSED */
+ pcb->err_code = PPPERR_AUTHFAIL;
+ lcp_close(pcb, "Authentication failed");
+}
+
+/*
+ * The peer has been successfully authenticated using `protocol'.
+ */
+void auth_peer_success(ppp_pcb *pcb, int protocol, int prot_flavor, const char *name, int namelen) {
+ int bit;
+#ifndef HAVE_MULTILINK
+ LWIP_UNUSED_ARG(name);
+ LWIP_UNUSED_ARG(namelen);
+#endif /* HAVE_MULTILINK */
+
+ switch (protocol) {
+#if CHAP_SUPPORT
+ case PPP_CHAP:
+ bit = CHAP_PEER;
+ switch (prot_flavor) {
+ case CHAP_MD5:
+ bit |= CHAP_MD5_PEER;
+ break;
+#if MSCHAP_SUPPORT
+ case CHAP_MICROSOFT:
+ bit |= CHAP_MS_PEER;
+ break;
+ case CHAP_MICROSOFT_V2:
+ bit |= CHAP_MS2_PEER;
+ break;
+#endif /* MSCHAP_SUPPORT */
+ default:
+ break;
+ }
+ break;
+#endif /* CHAP_SUPPORT */
+#if PAP_SUPPORT
+ case PPP_PAP:
+ bit = PAP_PEER;
+ break;
+#endif /* PAP_SUPPORT */
+#if EAP_SUPPORT
+ case PPP_EAP:
+ bit = EAP_PEER;
+ break;
+#endif /* EAP_SUPPORT */
+ default:
+ ppp_warn("auth_peer_success: unknown protocol %x", protocol);
+ return;
+ }
+
+#ifdef HAVE_MULTILINK
+ /*
+ * Save the authenticated name of the peer for later.
+ */
+ if (namelen > (int)sizeof(pcb->peer_authname) - 1)
+ namelen = (int)sizeof(pcb->peer_authname) - 1;
+ MEMCPY(pcb->peer_authname, name, namelen);
+ pcb->peer_authname[namelen] = 0;
+#endif /* HAVE_MULTILINK */
+#if 0 /* UNUSED */
+ script_setenv("PEERNAME", , 0);
+#endif /* UNUSED */
+
+ /* Save the authentication method for later. */
+ pcb->auth_done |= bit;
+
+ /*
+ * If there is no more authentication still to be done,
+ * proceed to the network (or callback) phase.
+ */
+ if ((pcb->auth_pending &= ~bit) == 0)
+ network_phase(pcb);
+}
+#endif /* PPP_SERVER */
+
+/*
+ * We have failed to authenticate ourselves to the peer using `protocol'.
+ */
+void auth_withpeer_fail(ppp_pcb *pcb, int protocol) {
+ LWIP_UNUSED_ARG(protocol);
+ /*
+ * We've failed to authenticate ourselves to our peer.
+ *
+ * Some servers keep sending CHAP challenges, but there
+ * is no point in persisting without any way to get updated
+ * authentication secrets.
+ *
+ * He'll probably take the link down, and there's not much
+ * we can do except wait for that.
+ */
+ pcb->err_code = PPPERR_AUTHFAIL;
+ lcp_close(pcb, "Failed to authenticate ourselves to peer");
+}
+
+/*
+ * We have successfully authenticated ourselves with the peer using `protocol'.
+ */
+void auth_withpeer_success(ppp_pcb *pcb, int protocol, int prot_flavor) {
+ int bit;
+ const char *prot = "";
+
+ switch (protocol) {
+#if CHAP_SUPPORT
+ case PPP_CHAP:
+ bit = CHAP_WITHPEER;
+ prot = "CHAP";
+ switch (prot_flavor) {
+ case CHAP_MD5:
+ bit |= CHAP_MD5_WITHPEER;
+ break;
+#if MSCHAP_SUPPORT
+ case CHAP_MICROSOFT:
+ bit |= CHAP_MS_WITHPEER;
+ break;
+ case CHAP_MICROSOFT_V2:
+ bit |= CHAP_MS2_WITHPEER;
+ break;
+#endif /* MSCHAP_SUPPORT */
+ default:
+ break;
+ }
+ break;
+#endif /* CHAP_SUPPORT */
+#if PAP_SUPPORT
+ case PPP_PAP:
+ bit = PAP_WITHPEER;
+ prot = "PAP";
+ break;
+#endif /* PAP_SUPPORT */
+#if EAP_SUPPORT
+ case PPP_EAP:
+ bit = EAP_WITHPEER;
+ prot = "EAP";
+ break;
+#endif /* EAP_SUPPORT */
+ default:
+ ppp_warn("auth_withpeer_success: unknown protocol %x", protocol);
+ bit = 0;
+ /* no break */
+ }
+
+ ppp_notice("%s authentication succeeded", prot);
+
+ /* Save the authentication method for later. */
+ pcb->auth_done |= bit;
+
+ /*
+ * If there is no more authentication still being done,
+ * proceed to the network (or callback) phase.
+ */
+ if ((pcb->auth_pending &= ~bit) == 0)
+ network_phase(pcb);
+}
+#endif /* PPP_AUTH_SUPPORT */
+
+
+/*
+ * np_up - a network protocol has come up.
+ */
+void np_up(ppp_pcb *pcb, int proto) {
+#if PPP_IDLETIMELIMIT
+ int tlim;
+#endif /* PPP_IDLETIMELIMIT */
+ LWIP_UNUSED_ARG(proto);
+
+ if (pcb->num_np_up == 0) {
+ /*
+ * At this point we consider that the link has come up successfully.
+ */
+ new_phase(pcb, PPP_PHASE_RUNNING);
+
+#if PPP_IDLETIMELIMIT
+#if 0 /* UNUSED */
+ if (idle_time_hook != 0)
+ tlim = (*idle_time_hook)(NULL);
+ else
+#endif /* UNUSED */
+ tlim = pcb->settings.idle_time_limit;
+ if (tlim > 0)
+ TIMEOUT(check_idle, (void*)pcb, tlim);
+#endif /* PPP_IDLETIMELIMIT */
+
+#if PPP_MAXCONNECT
+ /*
+ * Set a timeout to close the connection once the maximum
+ * connect time has expired.
+ */
+ if (pcb->settings.maxconnect > 0)
+ TIMEOUT(connect_time_expired, (void*)pcb, pcb->settings.maxconnect);
+#endif /* PPP_MAXCONNECT */
+
+#ifdef MAXOCTETS
+ if (maxoctets > 0)
+ TIMEOUT(check_maxoctets, NULL, maxoctets_timeout);
+#endif
+
+#if 0 /* Unused */
+ /*
+ * Detach now, if the updetach option was given.
+ */
+ if (updetach && !nodetach)
+ detach();
+#endif /* Unused */
+ }
+ ++pcb->num_np_up;
+}
+
+/*
+ * np_down - a network protocol has gone down.
+ */
+void np_down(ppp_pcb *pcb, int proto) {
+ LWIP_UNUSED_ARG(proto);
+ if (--pcb->num_np_up == 0) {
+#if PPP_IDLETIMELIMIT
+ UNTIMEOUT(check_idle, (void*)pcb);
+#endif /* PPP_IDLETIMELIMIT */
+#if PPP_MAXCONNECT
+ UNTIMEOUT(connect_time_expired, NULL);
+#endif /* PPP_MAXCONNECT */
+#ifdef MAXOCTETS
+ UNTIMEOUT(check_maxoctets, NULL);
+#endif
+ new_phase(pcb, PPP_PHASE_NETWORK);
+ }
+}
+
+/*
+ * np_finished - a network protocol has finished using the link.
+ */
+void np_finished(ppp_pcb *pcb, int proto) {
+ LWIP_UNUSED_ARG(proto);
+ if (--pcb->num_np_open <= 0) {
+ /* no further use for the link: shut up shop. */
+ lcp_close(pcb, "No network protocols running");
+ }
+}
+
+#ifdef MAXOCTETS
+static void
+check_maxoctets(arg)
+ void *arg;
+{
+#if PPP_STATS_SUPPORT
+ unsigned int used;
+
+ update_link_stats(ifunit);
+ link_stats_valid=0;
+
+ switch(maxoctets_dir) {
+ case PPP_OCTETS_DIRECTION_IN:
+ used = link_stats.bytes_in;
+ break;
+ case PPP_OCTETS_DIRECTION_OUT:
+ used = link_stats.bytes_out;
+ break;
+ case PPP_OCTETS_DIRECTION_MAXOVERAL:
+ case PPP_OCTETS_DIRECTION_MAXSESSION:
+ used = (link_stats.bytes_in > link_stats.bytes_out) ? link_stats.bytes_in : link_stats.bytes_out;
+ break;
+ default:
+ used = link_stats.bytes_in+link_stats.bytes_out;
+ break;
+ }
+ if (used > maxoctets) {
+ ppp_notice("Traffic limit reached. Limit: %u Used: %u", maxoctets, used);
+ status = EXIT_TRAFFIC_LIMIT;
+ lcp_close(pcb, "Traffic limit");
+#if 0 /* UNUSED */
+ need_holdoff = 0;
+#endif /* UNUSED */
+ } else {
+ TIMEOUT(check_maxoctets, NULL, maxoctets_timeout);
+ }
+#endif /* PPP_STATS_SUPPORT */
+}
+#endif /* MAXOCTETS */
+
+#if PPP_IDLETIMELIMIT
+/*
+ * check_idle - check whether the link has been idle for long
+ * enough that we can shut it down.
+ */
+static void check_idle(void *arg) {
+ ppp_pcb *pcb = (ppp_pcb*)arg;
+ struct ppp_idle idle;
+ time_t itime;
+ int tlim;
+
+ if (!get_idle_time(pcb, &idle))
+ return;
+#if 0 /* UNUSED */
+ if (idle_time_hook != 0) {
+ tlim = idle_time_hook(&idle);
+ } else {
+#endif /* UNUSED */
+ itime = LWIP_MIN(idle.xmit_idle, idle.recv_idle);
+ tlim = pcb->settings.idle_time_limit - itime;
+#if 0 /* UNUSED */
+ }
+#endif /* UNUSED */
+ if (tlim <= 0) {
+ /* link is idle: shut it down. */
+ ppp_notice("Terminating connection due to lack of activity.");
+ pcb->err_code = PPPERR_IDLETIMEOUT;
+ lcp_close(pcb, "Link inactive");
+#if 0 /* UNUSED */
+ need_holdoff = 0;
+#endif /* UNUSED */
+ } else {
+ TIMEOUT(check_idle, (void*)pcb, tlim);
+ }
+}
+#endif /* PPP_IDLETIMELIMIT */
+
+#if PPP_MAXCONNECT
+/*
+ * connect_time_expired - log a message and close the connection.
+ */
+static void connect_time_expired(void *arg) {
+ ppp_pcb *pcb = (ppp_pcb*)arg;
+ ppp_info("Connect time expired");
+ pcb->err_code = PPPERR_CONNECTTIME;
+ lcp_close(pcb, "Connect time expired"); /* Close connection */
+}
+#endif /* PPP_MAXCONNECT */
+
+#if PPP_OPTIONS
+/*
+ * auth_check_options - called to check authentication options.
+ */
+void
+auth_check_options()
+{
+ lcp_options *wo = &lcp_wantoptions[0];
+ int can_auth;
+ int lacks_ip;
+
+ /* Default our_name to hostname, and user to our_name */
+ if (our_name[0] == 0 || usehostname)
+ strlcpy(our_name, hostname, sizeof(our_name));
+ /* If a blank username was explicitly given as an option, trust
+ the user and don't use our_name */
+ if (ppp_settings.user[0] == 0 && !explicit_user)
+ strlcpy(ppp_settings.user, our_name, sizeof(ppp_settings.user));
+
+ /*
+ * If we have a default route, require the peer to authenticate
+ * unless the noauth option was given or the real user is root.
+ */
+ if (!auth_required && !allow_any_ip && have_route_to(0) && !privileged) {
+ auth_required = 1;
+ default_auth = 1;
+ }
+
+#if CHAP_SUPPORT
+ /* If we selected any CHAP flavors, we should probably negotiate it. :-) */
+ if (wo->chap_mdtype)
+ wo->neg_chap = 1;
+#endif /* CHAP_SUPPORT */
+
+ /* If authentication is required, ask peer for CHAP, PAP, or EAP. */
+ if (auth_required) {
+ allow_any_ip = 0;
+ if (1
+#if CHAP_SUPPORT
+ && !wo->neg_chap
+#endif /* CHAP_SUPPORT */
+#if PAP_SUPPORT
+ && !wo->neg_upap
+#endif /* PAP_SUPPORT */
+#if EAP_SUPPORT
+ && !wo->neg_eap
+#endif /* EAP_SUPPORT */
+ ) {
+#if CHAP_SUPPORT
+ wo->neg_chap = CHAP_MDTYPE_SUPPORTED != MDTYPE_NONE;
+ wo->chap_mdtype = CHAP_MDTYPE_SUPPORTED;
+#endif /* CHAP_SUPPORT */
+#if PAP_SUPPORT
+ wo->neg_upap = 1;
+#endif /* PAP_SUPPORT */
+#if EAP_SUPPORT
+ wo->neg_eap = 1;
+#endif /* EAP_SUPPORT */
+ }
+ } else {
+#if CHAP_SUPPORT
+ wo->neg_chap = 0;
+ wo->chap_mdtype = MDTYPE_NONE;
+#endif /* CHAP_SUPPORT */
+#if PAP_SUPPORT
+ wo->neg_upap = 0;
+#endif /* PAP_SUPPORT */
+#if EAP_SUPPORT
+ wo->neg_eap = 0;
+#endif /* EAP_SUPPORT */
+ }
+
+ /*
+ * Check whether we have appropriate secrets to use
+ * to authenticate the peer. Note that EAP can authenticate by way
+ * of a CHAP-like exchanges as well as SRP.
+ */
+ lacks_ip = 0;
+#if PAP_SUPPORT
+ can_auth = wo->neg_upap && (uselogin || have_pap_secret(&lacks_ip));
+#else
+ can_auth = 0;
+#endif /* PAP_SUPPORT */
+ if (!can_auth && (0
+#if CHAP_SUPPORT
+ || wo->neg_chap
+#endif /* CHAP_SUPPORT */
+#if EAP_SUPPORT
+ || wo->neg_eap
+#endif /* EAP_SUPPORT */
+ )) {
+#if CHAP_SUPPORT
+ can_auth = have_chap_secret((explicit_remote? remote_name: NULL),
+ our_name, 1, &lacks_ip);
+#else
+ can_auth = 0;
+#endif
+ }
+ if (!can_auth
+#if EAP_SUPPORT
+ && wo->neg_eap
+#endif /* EAP_SUPPORT */
+ ) {
+ can_auth = have_srp_secret((explicit_remote? remote_name: NULL),
+ our_name, 1, &lacks_ip);
+ }
+
+ if (auth_required && !can_auth && noauth_addrs == NULL) {
+ if (default_auth) {
+ option_error(
+"By default the remote system is required to authenticate itself");
+ option_error(
+"(because this system has a default route to the internet)");
+ } else if (explicit_remote)
+ option_error(
+"The remote system (%s) is required to authenticate itself",
+ remote_name);
+ else
+ option_error(
+"The remote system is required to authenticate itself");
+ option_error(
+"but I couldn't find any suitable secret (password) for it to use to do so.");
+ if (lacks_ip)
+ option_error(
+"(None of the available passwords would let it use an IP address.)");
+
+ exit(1);
+ }
+
+ /*
+ * Early check for remote number authorization.
+ */
+ if (!auth_number()) {
+ ppp_warn("calling number %q is not authorized", remote_number);
+ exit(EXIT_CNID_AUTH_FAILED);
+ }
+}
+#endif /* PPP_OPTIONS */
+
+#if 0 /* 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(unit)
+ int unit;
+{
+ lcp_options *go = &lcp_gotoptions[unit];
+ lcp_options *ao = &lcp_allowoptions[unit];
+ int hadchap;
+
+ hadchap = -1;
+ ao->neg_upap = !refuse_pap && (passwd[0] != 0 || get_pap_passwd(NULL));
+ ao->neg_chap = (!refuse_chap || !refuse_mschap || !refuse_mschap_v2)
+ && (passwd[0] != 0 ||
+ (hadchap = have_chap_secret(user, (explicit_remote? remote_name:
+ NULL), 0, NULL)));
+ ao->neg_eap = !refuse_eap && (
+ passwd[0] != 0 ||
+ (hadchap == 1 || (hadchap == -1 && have_chap_secret(user,
+ (explicit_remote? remote_name: NULL), 0, NULL))) ||
+ have_srp_secret(user, (explicit_remote? remote_name: NULL), 0, NULL));
+
+ hadchap = -1;
+ if (go->neg_upap && !uselogin && !have_pap_secret(NULL))
+ go->neg_upap = 0;
+ if (go->neg_chap) {
+ if (!(hadchap = have_chap_secret((explicit_remote? remote_name: NULL),
+ our_name, 1, NULL)))
+ go->neg_chap = 0;
+ }
+ if (go->neg_eap &&
+ (hadchap == 0 || (hadchap == -1 &&
+ !have_chap_secret((explicit_remote? remote_name: NULL), our_name,
+ 1, NULL))) &&
+ !have_srp_secret((explicit_remote? remote_name: NULL), our_name, 1,
+ NULL))
+ go->neg_eap = 0;
+}
+
+/*
+ * 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.
+ */
+int
+check_passwd(unit, auser, userlen, apasswd, passwdlen, msg)
+ int unit;
+ char *auser;
+ int userlen;
+ char *apasswd;
+ int passwdlen;
+ char **msg;
+{
+ return UPAP_AUTHNAK;
+ int ret;
+ char *filename;
+ FILE *f;
+ struct wordlist *addrs = NULL, *opts = NULL;
+ char passwd[256], user[256];
+ char secret[MAXWORDLEN];
+ static int attempts = 0;
+
+ /*
+ * Make copies of apasswd and auser, then null-terminate them.
+ * If there are unprintable characters in the password, make
+ * them visible.
+ */
+ slprintf(ppp_settings.passwd, sizeof(ppp_settings.passwd), "%.*v", passwdlen, apasswd);
+ slprintf(ppp_settings.user, sizeof(ppp_settings.user), "%.*v", userlen, auser);
+ *msg = "";
+
+ /*
+ * Check if a plugin wants to handle this.
+ */
+ if (pap_auth_hook) {
+ ret = (*pap_auth_hook)(ppp_settings.user, ppp_settings.passwd, msg, &addrs, &opts);
+ if (ret >= 0) {
+ /* note: set_allowed_addrs() saves opts (but not addrs):
+ don't free it! */
+ if (ret)
+ set_allowed_addrs(unit, addrs, opts);
+ else if (opts != 0)
+ free_wordlist(opts);
+ if (addrs != 0)
+ free_wordlist(addrs);
+ BZERO(ppp_settings.passwd, sizeof(ppp_settings.passwd));
+ return ret? UPAP_AUTHACK: UPAP_AUTHNAK;
+ }
+ }
+
+ /*
+ * Open the file of pap secrets and scan for a suitable secret
+ * for authenticating this user.
+ */
+ filename = _PATH_UPAPFILE;
+ addrs = opts = NULL;
+ ret = UPAP_AUTHNAK;
+ f = fopen(filename, "r");
+ if (f == NULL) {
+ ppp_error("Can't open PAP password file %s: %m", filename);
+
+ } else {
+ check_access(f, filename);
+ if (scan_authfile(f, ppp_settings.user, our_name, secret, &addrs, &opts, filename, 0) < 0) {
+ ppp_warn("no PAP secret found for %s", user);
+ } else {
+ /*
+ * If the secret is "@login", it means to check
+ * the password against the login database.
+ */
+ int login_secret = strcmp(secret, "@login") == 0;
+ ret = UPAP_AUTHACK;
+ if (uselogin || login_secret) {
+ /* login option or secret is @login */
+ if (session_full(ppp_settings.user, ppp_settings.passwd, devnam, msg) == 0) {
+ ret = UPAP_AUTHNAK;
+ }
+ } else if (session_mgmt) {
+ if (session_check(ppp_settings.user, NULL, devnam, NULL) == 0) {
+ ppp_warn("Peer %q failed PAP Session verification", user);
+ ret = UPAP_AUTHNAK;
+ }
+ }
+ if (secret[0] != 0 && !login_secret) {
+ /* password given in pap-secrets - must match */
+ if ((cryptpap || strcmp(ppp_settings.passwd, secret) != 0)
+ && strcmp(crypt(ppp_settings.passwd, secret), secret) != 0)
+ ret = UPAP_AUTHNAK;
+ }
+ }
+ fclose(f);
+ }
+
+ if (ret == UPAP_AUTHNAK) {
+ if (**msg == 0)
+ *msg = "Login incorrect";
+ /*
+ * XXX can we ever get here more than once??
+ * 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) {
+ ppp_warn("%d LOGIN FAILURES ON %s, %s", attempts, devnam, user);
+ lcp_close(pcb, "login failed");
+ }
+ if (attempts > 3)
+ sleep((u_int) (attempts - 3) * 5);
+ if (opts != NULL)
+ free_wordlist(opts);
+
+ } else {
+ attempts = 0; /* Reset count */
+ if (**msg == 0)
+ *msg = "Login ok";
+ set_allowed_addrs(unit, addrs, opts);
+ }
+
+ if (addrs != NULL)
+ free_wordlist(addrs);
+ BZERO(ppp_settings.passwd, sizeof(ppp_settings.passwd));
+ BZERO(secret, sizeof(secret));
+
+ return ret;
+}
+
+/*
+ * 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(unit)
+ int unit;
+{
+ char *filename;
+ FILE *f;
+ int i, ret;
+ struct wordlist *addrs, *opts;
+ char secret[MAXWORDLEN];
+
+ /*
+ * Check if a plugin wants to handle this.
+ */
+ ret = -1;
+ if (null_auth_hook)
+ ret = (*null_auth_hook)(&addrs, &opts);
+
+ /*
+ * Open the file of pap secrets and scan for a suitable secret.
+ */
+ if (ret <= 0) {
+ filename = _PATH_UPAPFILE;
+ addrs = NULL;
+ f = fopen(filename, "r");
+ if (f == NULL)
+ return 0;
+ check_access(f, filename);
+
+ i = scan_authfile(f, "", our_name, secret, &addrs, &opts, filename, 0);
+ ret = i >= 0 && secret[0] == 0;
+ BZERO(secret, sizeof(secret));
+ fclose(f);
+ }
+
+ if (ret)
+ set_allowed_addrs(unit, addrs, opts);
+ else if (opts != 0)
+ free_wordlist(opts);
+ if (addrs != 0)
+ free_wordlist(addrs);
+
+ return ret;
+}
+
+/*
+ * 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.
+ * Assumes passwd points to MAXSECRETLEN bytes of space (if non-null).
+ */
+static int
+get_pap_passwd(passwd)
+ char *passwd;
+{
+ char *filename;
+ FILE *f;
+ int ret;
+ char secret[MAXWORDLEN];
+
+ /*
+ * Check whether a plugin wants to supply this.
+ */
+ if (pap_passwd_hook) {
+ ret = (*pap_passwd_hook)(ppp_settings,user, ppp_settings.passwd);
+ if (ret >= 0)
+ return ret;
+ }
+
+ filename = _PATH_UPAPFILE;
+ f = fopen(filename, "r");
+ if (f == NULL)
+ return 0;
+ check_access(f, filename);
+ ret = scan_authfile(f, user,
+ (remote_name[0]? remote_name: NULL),
+ secret, NULL, NULL, filename, 0);
+ fclose(f);
+ if (ret < 0)
+ return 0;
+ if (passwd != NULL)
+ strlcpy(passwd, secret, MAXSECRETLEN);
+ BZERO(secret, sizeof(secret));
+ 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(lacks_ipp)
+ int *lacks_ipp;
+{
+ FILE *f;
+ int ret;
+ char *filename;
+ struct wordlist *addrs;
+
+ /* let the plugin decide, if there is one */
+ if (pap_check_hook) {
+ ret = (*pap_check_hook)();
+ if (ret >= 0)
+ return ret;
+ }
+
+ filename = _PATH_UPAPFILE;
+ f = fopen(filename, "r");
+ if (f == NULL)
+ return 0;
+
+ ret = scan_authfile(f, (explicit_remote? remote_name: NULL), our_name,
+ NULL, &addrs, NULL, filename, 0);
+ fclose(f);
+ if (ret >= 0 && !some_ip_ok(addrs)) {
+ if (lacks_ipp != 0)
+ *lacks_ipp = 1;
+ ret = -1;
+ }
+ if (addrs != 0)
+ free_wordlist(addrs);
+
+ return ret >= 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(client, server, need_ip, lacks_ipp)
+ char *client;
+ char *server;
+ int need_ip;
+ int *lacks_ipp;
+{
+ FILE *f;
+ int ret;
+ char *filename;
+ struct wordlist *addrs;
+
+ if (chap_check_hook) {
+ ret = (*chap_check_hook)();
+ if (ret >= 0) {
+ return ret;
+ }
+ }
+
+ filename = _PATH_CHAPFILE;
+ f = fopen(filename, "r");
+ if (f == NULL)
+ return 0;
+
+ if (client != NULL && client[0] == 0)
+ client = NULL;
+ else if (server != NULL && server[0] == 0)
+ server = NULL;
+
+ ret = scan_authfile(f, client, server, NULL, &addrs, NULL, filename, 0);
+ fclose(f);
+ if (ret >= 0 && need_ip && !some_ip_ok(addrs)) {
+ if (lacks_ipp != 0)
+ *lacks_ipp = 1;
+ ret = -1;
+ }
+ if (addrs != 0)
+ free_wordlist(addrs);
+
+ return ret >= 0;
+}
+
+/*
+ * have_srp_secret - check whether we have a SRP 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_srp_secret(client, server, need_ip, lacks_ipp)
+ char *client;
+ char *server;
+ int need_ip;
+ int *lacks_ipp;
+{
+ FILE *f;
+ int ret;
+ char *filename;
+ struct wordlist *addrs;
+
+ filename = _PATH_SRPFILE;
+ f = fopen(filename, "r");
+ if (f == NULL)
+ return 0;
+
+ if (client != NULL && client[0] == 0)
+ client = NULL;
+ else if (server != NULL && server[0] == 0)
+ server = NULL;
+
+ ret = scan_authfile(f, client, server, NULL, &addrs, NULL, filename, 0);
+ fclose(f);
+ if (ret >= 0 && need_ip && !some_ip_ok(addrs)) {
+ if (lacks_ipp != 0)
+ *lacks_ipp = 1;
+ ret = -1;
+ }
+ if (addrs != 0)
+ free_wordlist(addrs);
+
+ return ret >= 0;
+}
+#endif /* UNUSED */
+
+#if PPP_AUTH_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(ppp_pcb *pcb, const char *client, const char *server, char *secret, int *secret_len, int am_server) {
+ int len;
+ LWIP_UNUSED_ARG(server);
+ LWIP_UNUSED_ARG(am_server);
+
+ if (!client || !client[0] || !pcb->settings.user || !pcb->settings.passwd || strcmp(client, pcb->settings.user)) {
+ return 0;
+ }
+
+ len = (int)strlen(pcb->settings.passwd);
+ if (len > MAXSECRETLEN) {
+ ppp_error("Secret for %s on %s is too long", client, server);
+ len = MAXSECRETLEN;
+ }
+
+ MEMCPY(secret, pcb->settings.passwd, len);
+ *secret_len = len;
+ return 1;
+
+#if 0 /* UNUSED */
+ FILE *f;
+ int ret, len;
+ char *filename;
+ struct wordlist *addrs, *opts;
+ char secbuf[MAXWORDLEN];
+ struct wordlist *addrs;
+ addrs = NULL;
+
+ if (!am_server && ppp_settings.passwd[0] != 0) {
+ strlcpy(secbuf, ppp_settings.passwd, sizeof(secbuf));
+ } else if (!am_server && chap_passwd_hook) {
+ if ( (*chap_passwd_hook)(client, secbuf) < 0) {
+ ppp_error("Unable to obtain CHAP password for %s on %s from plugin",
+ client, server);
+ return 0;
+ }
+ } else {
+ filename = _PATH_CHAPFILE;
+ addrs = NULL;
+ secbuf[0] = 0;
+
+ f = fopen(filename, "r");
+ if (f == NULL) {
+ ppp_error("Can't open chap secret file %s: %m", filename);
+ return 0;
+ }
+ check_access(f, filename);
+
+ ret = scan_authfile(f, client, server, secbuf, &addrs, &opts, filename, 0);
+ fclose(f);
+ if (ret < 0)
+ return 0;
+
+ if (am_server)
+ set_allowed_addrs(unit, addrs, opts);
+ else if (opts != 0)
+ free_wordlist(opts);
+ if (addrs != 0)
+ free_wordlist(addrs);
+ }
+
+ len = strlen(secbuf);
+ if (len > MAXSECRETLEN) {
+ ppp_error("Secret for %s on %s is too long", client, server);
+ len = MAXSECRETLEN;
+ }
+ MEMCPY(secret, secbuf, len);
+ BZERO(secbuf, sizeof(secbuf));
+ *secret_len = len;
+
+ return 1;
+#endif /* UNUSED */
+}
+#endif /* PPP_AUTH_SUPPORT */
+
+
+#if 0 /* UNUSED */
+/*
+ * get_srp_secret - open the SRP secret file and return the secret
+ * for authenticating the given client on the given server.
+ * (We could be either client or server).
+ */
+int
+get_srp_secret(unit, client, server, secret, am_server)
+ int unit;
+ char *client;
+ char *server;
+ char *secret;
+ int am_server;
+{
+ FILE *fp;
+ int ret;
+ char *filename;
+ struct wordlist *addrs, *opts;
+
+ if (!am_server && ppp_settings.passwd[0] != '\0') {
+ strlcpy(secret, ppp_settings.passwd, MAXWORDLEN);
+ } else {
+ filename = _PATH_SRPFILE;
+ addrs = NULL;
+
+ fp = fopen(filename, "r");
+ if (fp == NULL) {
+ ppp_error("Can't open srp secret file %s: %m", filename);
+ return 0;
+ }
+ check_access(fp, filename);
+
+ secret[0] = '\0';
+ ret = scan_authfile(fp, client, server, secret, &addrs, &opts,
+ filename, am_server);
+ fclose(fp);
+ if (ret < 0)
+ return 0;
+
+ if (am_server)
+ set_allowed_addrs(unit, addrs, opts);
+ else if (opts != NULL)
+ free_wordlist(opts);
+ if (addrs != NULL)
+ free_wordlist(addrs);
+ }
+
+ return 1;
+}
+
+/*
+ * set_allowed_addrs() - set the list of allowed addresses.
+ * Also looks for `--' indicating options to apply for this peer
+ * and leaves the following words in extra_options.
+ */
+static void
+set_allowed_addrs(unit, addrs, opts)
+ int unit;
+ struct wordlist *addrs;
+ struct wordlist *opts;
+{
+ int n;
+ struct wordlist *ap, **plink;
+ struct permitted_ip *ip;
+ char *ptr_word, *ptr_mask;
+ struct hostent *hp;
+ struct netent *np;
+ u32_t a, mask, ah, offset;
+ struct ipcp_options *wo = &ipcp_wantoptions[unit];
+ u32_t suggested_ip = 0;
+
+ if (addresses[unit] != NULL)
+ free(addresses[unit]);
+ addresses[unit] = NULL;
+ if (extra_options != NULL)
+ free_wordlist(extra_options);
+ extra_options = opts;
+
+ /*
+ * Count the number of IP addresses given.
+ */
+ n = wordlist_count(addrs) + wordlist_count(noauth_addrs);
+ if (n == 0)
+ return;
+ ip = (struct permitted_ip *) malloc((n + 1) * sizeof(struct permitted_ip));
+ if (ip == 0)
+ return;
+
+ /* temporarily append the noauth_addrs list to addrs */
+ for (plink = &addrs; *plink != NULL; plink = &(*plink)->next)
+ ;
+ *plink = noauth_addrs;
+
+ n = 0;
+ for (ap = addrs; ap != NULL; ap = ap->next) {
+ /* "-" means no addresses authorized, "*" means any address allowed */
+ ptr_word = ap->word;
+ if (strcmp(ptr_word, "-") == 0)
+ break;
+ if (strcmp(ptr_word, "*") == 0) {
+ ip[n].permit = 1;
+ ip[n].base = ip[n].mask = 0;
+ ++n;
+ break;
+ }
+
+ ip[n].permit = 1;
+ if (*ptr_word == '!') {
+ ip[n].permit = 0;
+ ++ptr_word;
+ }
+
+ mask = ~ (u32_t) 0;
+ offset = 0;
+ ptr_mask = strchr (ptr_word, '/');
+ if (ptr_mask != NULL) {
+ int bit_count;
+ char *endp;
+
+ bit_count = (int) strtol (ptr_mask+1, &endp, 10);
+ if (bit_count <= 0 || bit_count > 32) {
+ ppp_warn("invalid address length %v in auth. address list",
+ ptr_mask+1);
+ continue;
+ }
+ bit_count = 32 - bit_count; /* # bits in host part */
+ if (*endp == '+') {
+ offset = ifunit + 1;
+ ++endp;
+ }
+ if (*endp != 0) {
+ ppp_warn("invalid address length syntax: %v", ptr_mask+1);
+ continue;
+ }
+ *ptr_mask = '\0';
+ mask <<= bit_count;
+ }
+
+ hp = gethostbyname(ptr_word);
+ if (hp != NULL && hp->h_addrtype == AF_INET) {
+ a = *(u32_t *)hp->h_addr;
+ } else {
+ np = getnetbyname (ptr_word);
+ if (np != NULL && np->n_addrtype == AF_INET) {
+ a = lwip_htonl ((u32_t)np->n_net);
+ if (ptr_mask == NULL) {
+ /* calculate appropriate mask for net */
+ ah = lwip_ntohl(a);
+ if (IN_CLASSA(ah))
+ mask = IN_CLASSA_NET;
+ else if (IN_CLASSB(ah))
+ mask = IN_CLASSB_NET;
+ else if (IN_CLASSC(ah))
+ mask = IN_CLASSC_NET;
+ }
+ } else {
+ a = inet_addr (ptr_word);
+ }
+ }
+
+ if (ptr_mask != NULL)
+ *ptr_mask = '/';
+
+ if (a == (u32_t)-1L) {
+ ppp_warn("unknown host %s in auth. address list", ap->word);
+ continue;
+ }
+ if (offset != 0) {
+ if (offset >= ~mask) {
+ ppp_warn("interface unit %d too large for subnet %v",
+ ifunit, ptr_word);
+ continue;
+ }
+ a = lwip_htonl((lwip_ntohl(a) & mask) + offset);
+ mask = ~(u32_t)0;
+ }
+ ip[n].mask = lwip_htonl(mask);
+ ip[n].base = a & ip[n].mask;
+ ++n;
+ if (~mask == 0 && suggested_ip == 0)
+ suggested_ip = a;
+ }
+ *plink = NULL;
+
+ ip[n].permit = 0; /* make the last entry forbid all addresses */
+ ip[n].base = 0; /* to terminate the list */
+ ip[n].mask = 0;
+
+ addresses[unit] = ip;
+
+ /*
+ * If the address given for the peer isn't authorized, or if
+ * the user hasn't given one, AND there is an authorized address
+ * which is a single host, then use that if we find one.
+ */
+ if (suggested_ip != 0
+ && (wo->hisaddr == 0 || !auth_ip_addr(unit, wo->hisaddr))) {
+ wo->hisaddr = suggested_ip;
+ /*
+ * Do we insist on this address? No, if there are other
+ * addresses authorized than the suggested one.
+ */
+ if (n > 1)
+ wo->accept_remote = 1;
+ }
+}
+
+/*
+ * 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(unit, addr)
+ int unit;
+ u32_t addr;
+{
+ int ok;
+
+ /* don't allow loopback or multicast address */
+ if (bad_ip_adrs(addr))
+ return 0;
+
+ if (allowed_address_hook) {
+ ok = allowed_address_hook(addr);
+ if (ok >= 0) return ok;
+ }
+
+ if (addresses[unit] != NULL) {
+ ok = ip_addr_check(addr, addresses[unit]);
+ if (ok >= 0)
+ return ok;
+ }
+
+ if (auth_required)
+ return 0; /* no addresses authorized */
+ return allow_any_ip || privileged || !have_route_to(addr);
+}
+
+static int
+ip_addr_check(addr, addrs)
+ u32_t addr;
+ struct permitted_ip *addrs;
+{
+ for (; ; ++addrs)
+ if ((addr & addrs->mask) == addrs->base)
+ return addrs->permit;
+}
+
+/*
+ * 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(addr)
+ u32_t addr;
+{
+ addr = lwip_ntohl(addr);
+ return (addr >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET
+ || IN_MULTICAST(addr) || IN_BADCLASS(addr);
+}
+
+/*
+ * some_ip_ok - check a wordlist to see if it authorizes any
+ * IP address(es).
+ */
+static int
+some_ip_ok(addrs)
+ 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;
+}
+
+/*
+ * auth_number - check whether the remote number is allowed to connect.
+ * Returns 1 if authorized, 0 otherwise.
+ */
+int
+auth_number()
+{
+ struct wordlist *wp = permitted_numbers;
+ int l;
+
+ /* Allow all if no authorization list. */
+ if (!wp)
+ return 1;
+
+ /* Allow if we have a match in the authorization list. */
+ while (wp) {
+ /* trailing '*' wildcard */
+ l = strlen(wp->word);
+ if ((wp->word)[l - 1] == '*')
+ l--;
+ if (!strncasecmp(wp->word, remote_number, l))
+ return 1;
+ wp = wp->next;
+ }
+
+ return 0;
+}
+
+/*
+ * check_access - complain if a secret file has too-liberal permissions.
+ */
+static void
+check_access(f, filename)
+ FILE *f;
+ char *filename;
+{
+ struct stat sbuf;
+
+ if (fstat(fileno(f), &sbuf) < 0) {
+ ppp_warn("cannot stat secret file %s: %m", filename);
+ } else if ((sbuf.st_mode & (S_IRWXG | S_IRWXO)) != 0) {
+ ppp_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.
+ * Flags are non-zero if we need two colons in the secret in order to
+ * match.
+ */
+static int
+scan_authfile(f, client, server, secret, addrs, opts, filename, flags)
+ FILE *f;
+ char *client;
+ char *server;
+ char *secret;
+ struct wordlist **addrs;
+ struct wordlist **opts;
+ char *filename;
+ int flags;
+{
+ int newline, xxx;
+ int got_flag, best_flag;
+ FILE *sf;
+ struct wordlist *ap, *addr_list, *alist, **app;
+ char word[MAXWORDLEN];
+ char atfile[MAXWORDLEN];
+ char lsecret[MAXWORDLEN];
+ char *cp;
+
+ if (addrs != NULL)
+ *addrs = NULL;
+ if (opts != NULL)
+ *opts = NULL;
+ addr_list = NULL;
+ if (!getword(f, word, &newline, filename))
+ return -1; /* file is empty??? */
+ newline = 1;
+ best_flag = -1;
+ for (;;) {
+ /*
+ * Skip until we find a word at the start of a line.
+ */
+ while (!newline && getword(f, word, &newline, filename))
+ ;
+ if (!newline)
+ break; /* got to end of file */
+
+ /*
+ * Got a client - check if it's a match or a wildcard.
+ */
+ got_flag = 0;
+ if (client != NULL && strcmp(word, client) != 0 && !ISWILD(word)) {
+ newline = 0;
+ continue;
+ }
+ if (!ISWILD(word))
+ got_flag = NONWILD_CLIENT;
+
+ /*
+ * Now get a server and check if it matches.
+ */
+ if (!getword(f, word, &newline, filename))
+ break;
+ if (newline)
+ continue;
+ if (!ISWILD(word)) {
+ if (server != NULL && strcmp(word, server) != 0)
+ continue;
+ got_flag |= NONWILD_SERVER;
+ }
+
+ /*
+ * Got some sort of a match - see if it's better than what
+ * we have already.
+ */
+ if (got_flag <= best_flag)
+ continue;
+
+ /*
+ * Get the secret.
+ */
+ if (!getword(f, word, &newline, filename))
+ break;
+ if (newline)
+ continue;
+
+ /*
+ * SRP-SHA1 authenticator should never be reading secrets from
+ * a file. (Authenticatee may, though.)
+ */
+ if (flags && ((cp = strchr(word, ':')) == NULL ||
+ strchr(cp + 1, ':') == NULL))
+ continue;
+
+ if (secret != NULL) {
+ /*
+ * Special syntax: @/pathname means read secret from file.
+ */
+ if (word[0] == '@' && word[1] == '/') {
+ strlcpy(atfile, word+1, sizeof(atfile));
+ if ((sf = fopen(atfile, "r")) == NULL) {
+ ppp_warn("can't open indirect secret file %s", atfile);
+ continue;
+ }
+ check_access(sf, atfile);
+ if (!getword(sf, word, &xxx, atfile)) {
+ ppp_warn("no secret in indirect secret file %s", atfile);
+ fclose(sf);
+ continue;
+ }
+ fclose(sf);
+ }
+ strlcpy(lsecret, word, sizeof(lsecret));
+ }
+
+ /*
+ * Now read address authorization info and make a wordlist.
+ */
+ app = &alist;
+ for (;;) {
+ if (!getword(f, word, &newline, filename) || newline)
+ break;
+ ap = (struct wordlist *)
+ malloc(sizeof(struct wordlist) + strlen(word) + 1);
+ if (ap == NULL)
+ novm("authorized addresses");
+ ap->word = (char *) (ap + 1);
+ strcpy(ap->word, word);
+ *app = ap;
+ app = &ap->next;
+ }
+ *app = NULL;
+
+ /*
+ * This is the best so far; remember it.
+ */
+ best_flag = got_flag;
+ if (addr_list)
+ free_wordlist(addr_list);
+ addr_list = alist;
+ if (secret != NULL)
+ strlcpy(secret, lsecret, MAXWORDLEN);
+
+ if (!newline)
+ break;
+ }
+
+ /* scan for a -- word indicating the start of options */
+ for (app = &addr_list; (ap = *app) != NULL; app = &ap->next)
+ if (strcmp(ap->word, "--") == 0)
+ break;
+ /* ap = start of options */
+ if (ap != NULL) {
+ ap = ap->next; /* first option */
+ free(*app); /* free the "--" word */
+ *app = NULL; /* terminate addr list */
+ }
+ if (opts != NULL)
+ *opts = ap;
+ else if (ap != NULL)
+ free_wordlist(ap);
+ if (addrs != NULL)
+ *addrs = addr_list;
+ else if (addr_list != NULL)
+ free_wordlist(addr_list);
+
+ return best_flag;
+}
+
+/*
+ * wordlist_count - return the number of items in a wordlist
+ */
+static int
+wordlist_count(wp)
+ struct wordlist *wp;
+{
+ int n;
+
+ for (n = 0; wp != NULL; wp = wp->next)
+ ++n;
+ return n;
+}
+
+/*
+ * free_wordlist - release memory allocated for a wordlist.
+ */
+static void
+free_wordlist(wp)
+ struct wordlist *wp;
+{
+ struct wordlist *next;
+
+ while (wp != NULL) {
+ next = wp->next;
+ free(wp);
+ wp = next;
+ }
+}
+#endif /* UNUSED */
+
+#endif /* PPP_SUPPORT */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/ccp.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/ccp.c
new file mode 100644
index 0000000..f8519eb
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/ccp.c
@@ -0,0 +1,1740 @@
+/*
+ * ccp.c - PPP Compression Control Protocol.
+ *
+ * Copyright (c) 1994-2002 Paul Mackerras. 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. The name(s) of the authors of this software must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission.
+ *
+ * 3. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by Paul Mackerras
+ * <paulus@samba.org>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT && CCP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "netif/ppp/ppp_impl.h"
+
+#include "netif/ppp/fsm.h"
+#include "netif/ppp/ccp.h"
+
+#if MPPE_SUPPORT
+#include "netif/ppp/lcp.h" /* lcp_close(), lcp_fsm */
+#include "netif/ppp/mppe.h" /* mppe_init() */
+#endif /* MPPE_SUPPORT */
+
+/*
+ * Unfortunately there is a bug in zlib which means that using a
+ * size of 8 (window size = 256) for Deflate compression will cause
+ * buffer overruns and kernel crashes in the deflate module.
+ * Until this is fixed we only accept sizes in the range 9 .. 15.
+ * Thanks to James Carlson for pointing this out.
+ */
+#define DEFLATE_MIN_WORKS 9
+
+/*
+ * Command-line options.
+ */
+#if PPP_OPTIONS
+static int setbsdcomp (char **);
+static int setdeflate (char **);
+static char bsd_value[8];
+static char deflate_value[8];
+
+/*
+ * Option variables.
+ */
+#if MPPE_SUPPORT
+bool refuse_mppe_stateful = 1; /* Allow stateful mode? */
+#endif /* MPPE_SUPPORT */
+
+static option_t ccp_option_list[] = {
+ { "noccp", o_bool, &ccp_protent.enabled_flag,
+ "Disable CCP negotiation" },
+ { "-ccp", o_bool, &ccp_protent.enabled_flag,
+ "Disable CCP negotiation", OPT_ALIAS },
+
+ { "bsdcomp", o_special, (void *)setbsdcomp,
+ "Request BSD-Compress packet compression",
+ OPT_PRIO | OPT_A2STRVAL | OPT_STATIC, bsd_value },
+ { "nobsdcomp", o_bool, &ccp_wantoptions[0].bsd_compress,
+ "don't allow BSD-Compress", OPT_PRIOSUB | OPT_A2CLR,
+ &ccp_allowoptions[0].bsd_compress },
+ { "-bsdcomp", o_bool, &ccp_wantoptions[0].bsd_compress,
+ "don't allow BSD-Compress", OPT_ALIAS | OPT_PRIOSUB | OPT_A2CLR,
+ &ccp_allowoptions[0].bsd_compress },
+
+ { "deflate", o_special, (void *)setdeflate,
+ "request Deflate compression",
+ OPT_PRIO | OPT_A2STRVAL | OPT_STATIC, deflate_value },
+ { "nodeflate", o_bool, &ccp_wantoptions[0].deflate,
+ "don't allow Deflate compression", OPT_PRIOSUB | OPT_A2CLR,
+ &ccp_allowoptions[0].deflate },
+ { "-deflate", o_bool, &ccp_wantoptions[0].deflate,
+ "don't allow Deflate compression", OPT_ALIAS | OPT_PRIOSUB | OPT_A2CLR,
+ &ccp_allowoptions[0].deflate },
+
+ { "nodeflatedraft", o_bool, &ccp_wantoptions[0].deflate_draft,
+ "don't use draft deflate #", OPT_A2COPY,
+ &ccp_allowoptions[0].deflate_draft },
+
+ { "predictor1", o_bool, &ccp_wantoptions[0].predictor_1,
+ "request Predictor-1", OPT_PRIO | 1 },
+ { "nopredictor1", o_bool, &ccp_wantoptions[0].predictor_1,
+ "don't allow Predictor-1", OPT_PRIOSUB | OPT_A2CLR,
+ &ccp_allowoptions[0].predictor_1 },
+ { "-predictor1", o_bool, &ccp_wantoptions[0].predictor_1,
+ "don't allow Predictor-1", OPT_ALIAS | OPT_PRIOSUB | OPT_A2CLR,
+ &ccp_allowoptions[0].predictor_1 },
+
+#if MPPE_SUPPORT
+ /* MPPE options are symmetrical ... we only set wantoptions here */
+ { "require-mppe", o_bool, &ccp_wantoptions[0].mppe,
+ "require MPPE encryption",
+ OPT_PRIO | MPPE_OPT_40 | MPPE_OPT_128 },
+ { "+mppe", o_bool, &ccp_wantoptions[0].mppe,
+ "require MPPE encryption",
+ OPT_ALIAS | OPT_PRIO | MPPE_OPT_40 | MPPE_OPT_128 },
+ { "nomppe", o_bool, &ccp_wantoptions[0].mppe,
+ "don't allow MPPE encryption", OPT_PRIO },
+ { "-mppe", o_bool, &ccp_wantoptions[0].mppe,
+ "don't allow MPPE encryption", OPT_ALIAS | OPT_PRIO },
+
+ /* We use ccp_allowoptions[0].mppe as a junk var ... it is reset later */
+ { "require-mppe-40", o_bool, &ccp_allowoptions[0].mppe,
+ "require MPPE 40-bit encryption", OPT_PRIO | OPT_A2OR | MPPE_OPT_40,
+ &ccp_wantoptions[0].mppe },
+ { "+mppe-40", o_bool, &ccp_allowoptions[0].mppe,
+ "require MPPE 40-bit encryption", OPT_PRIO | OPT_A2OR | MPPE_OPT_40,
+ &ccp_wantoptions[0].mppe },
+ { "nomppe-40", o_bool, &ccp_allowoptions[0].mppe,
+ "don't allow MPPE 40-bit encryption",
+ OPT_PRIOSUB | OPT_A2CLRB | MPPE_OPT_40, &ccp_wantoptions[0].mppe },
+ { "-mppe-40", o_bool, &ccp_allowoptions[0].mppe,
+ "don't allow MPPE 40-bit encryption",
+ OPT_ALIAS | OPT_PRIOSUB | OPT_A2CLRB | MPPE_OPT_40,
+ &ccp_wantoptions[0].mppe },
+
+ { "require-mppe-128", o_bool, &ccp_allowoptions[0].mppe,
+ "require MPPE 128-bit encryption", OPT_PRIO | OPT_A2OR | MPPE_OPT_128,
+ &ccp_wantoptions[0].mppe },
+ { "+mppe-128", o_bool, &ccp_allowoptions[0].mppe,
+ "require MPPE 128-bit encryption",
+ OPT_ALIAS | OPT_PRIO | OPT_A2OR | MPPE_OPT_128,
+ &ccp_wantoptions[0].mppe },
+ { "nomppe-128", o_bool, &ccp_allowoptions[0].mppe,
+ "don't allow MPPE 128-bit encryption",
+ OPT_PRIOSUB | OPT_A2CLRB | MPPE_OPT_128, &ccp_wantoptions[0].mppe },
+ { "-mppe-128", o_bool, &ccp_allowoptions[0].mppe,
+ "don't allow MPPE 128-bit encryption",
+ OPT_ALIAS | OPT_PRIOSUB | OPT_A2CLRB | MPPE_OPT_128,
+ &ccp_wantoptions[0].mppe },
+
+ /* strange one; we always request stateless, but will we allow stateful? */
+ { "mppe-stateful", o_bool, &refuse_mppe_stateful,
+ "allow MPPE stateful mode", OPT_PRIO },
+ { "nomppe-stateful", o_bool, &refuse_mppe_stateful,
+ "disallow MPPE stateful mode", OPT_PRIO | 1 },
+#endif /* MPPE_SUPPORT */
+
+ { NULL }
+};
+#endif /* PPP_OPTIONS */
+
+/*
+ * Protocol entry points from main code.
+ */
+static void ccp_init(ppp_pcb *pcb);
+static void ccp_open(ppp_pcb *pcb);
+static void ccp_close(ppp_pcb *pcb, const char *reason);
+static void ccp_lowerup(ppp_pcb *pcb);
+static void ccp_lowerdown(ppp_pcb *pcb);
+static void ccp_input(ppp_pcb *pcb, u_char *pkt, int len);
+static void ccp_protrej(ppp_pcb *pcb);
+#if PRINTPKT_SUPPORT
+static int ccp_printpkt(const u_char *p, int plen, void (*printer) (void *, const char *, ...), void *arg);
+#endif /* PRINTPKT_SUPPORT */
+#if PPP_DATAINPUT
+static void ccp_datainput(ppp_pcb *pcb, u_char *pkt, int len);
+#endif /* PPP_DATAINPUT */
+
+const struct protent ccp_protent = {
+ PPP_CCP,
+ ccp_init,
+ ccp_input,
+ ccp_protrej,
+ ccp_lowerup,
+ ccp_lowerdown,
+ ccp_open,
+ ccp_close,
+#if PRINTPKT_SUPPORT
+ ccp_printpkt,
+#endif /* PRINTPKT_SUPPORT */
+#if PPP_DATAINPUT
+ ccp_datainput,
+#endif /* PPP_DATAINPUT */
+#if PRINTPKT_SUPPORT
+ "CCP",
+ "Compressed",
+#endif /* PRINTPKT_SUPPORT */
+#if PPP_OPTIONS
+ ccp_option_list,
+ NULL,
+#endif /* PPP_OPTIONS */
+#if DEMAND_SUPPORT
+ NULL,
+ NULL
+#endif /* DEMAND_SUPPORT */
+};
+
+/*
+ * Callbacks for fsm code.
+ */
+static void ccp_resetci (fsm *);
+static int ccp_cilen (fsm *);
+static void ccp_addci (fsm *, u_char *, int *);
+static int ccp_ackci (fsm *, u_char *, int);
+static int ccp_nakci (fsm *, u_char *, int, int);
+static int ccp_rejci (fsm *, u_char *, int);
+static int ccp_reqci (fsm *, u_char *, int *, int);
+static void ccp_up (fsm *);
+static void ccp_down (fsm *);
+static int ccp_extcode (fsm *, int, int, u_char *, int);
+static void ccp_rack_timeout (void *);
+static const char *method_name (ccp_options *, ccp_options *);
+
+static const fsm_callbacks ccp_callbacks = {
+ ccp_resetci,
+ ccp_cilen,
+ ccp_addci,
+ ccp_ackci,
+ ccp_nakci,
+ ccp_rejci,
+ ccp_reqci,
+ ccp_up,
+ ccp_down,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ ccp_extcode,
+ "CCP"
+};
+
+/*
+ * Do we want / did we get any compression?
+ */
+static int ccp_anycompress(ccp_options *opt) {
+ return (0
+#if DEFLATE_SUPPORT
+ || (opt)->deflate
+#endif /* DEFLATE_SUPPORT */
+#if BSDCOMPRESS_SUPPORT
+ || (opt)->bsd_compress
+#endif /* BSDCOMPRESS_SUPPORT */
+#if PREDICTOR_SUPPORT
+ || (opt)->predictor_1 || (opt)->predictor_2
+#endif /* PREDICTOR_SUPPORT */
+#if MPPE_SUPPORT
+ || (opt)->mppe
+#endif /* MPPE_SUPPORT */
+ );
+}
+
+/*
+ * Local state (mainly for handling reset-reqs and reset-acks).
+ */
+#define RACK_PENDING 1 /* waiting for reset-ack */
+#define RREQ_REPEAT 2 /* send another reset-req if no reset-ack */
+
+#define RACKTIMEOUT 1 /* second */
+
+#if PPP_OPTIONS
+/*
+ * Option parsing
+ */
+static int
+setbsdcomp(argv)
+ char **argv;
+{
+ int rbits, abits;
+ char *str, *endp;
+
+ str = *argv;
+ abits = rbits = strtol(str, &endp, 0);
+ if (endp != str && *endp == ',') {
+ str = endp + 1;
+ abits = strtol(str, &endp, 0);
+ }
+ if (*endp != 0 || endp == str) {
+ option_error("invalid parameter '%s' for bsdcomp option", *argv);
+ return 0;
+ }
+ if ((rbits != 0 && (rbits < BSD_MIN_BITS || rbits > BSD_MAX_BITS))
+ || (abits != 0 && (abits < BSD_MIN_BITS || abits > BSD_MAX_BITS))) {
+ option_error("bsdcomp option values must be 0 or %d .. %d",
+ BSD_MIN_BITS, BSD_MAX_BITS);
+ return 0;
+ }
+ if (rbits > 0) {
+ ccp_wantoptions[0].bsd_compress = 1;
+ ccp_wantoptions[0].bsd_bits = rbits;
+ } else
+ ccp_wantoptions[0].bsd_compress = 0;
+ if (abits > 0) {
+ ccp_allowoptions[0].bsd_compress = 1;
+ ccp_allowoptions[0].bsd_bits = abits;
+ } else
+ ccp_allowoptions[0].bsd_compress = 0;
+ ppp_slprintf(bsd_value, sizeof(bsd_value),
+ rbits == abits? "%d": "%d,%d", rbits, abits);
+
+ return 1;
+}
+
+static int
+setdeflate(argv)
+ char **argv;
+{
+ int rbits, abits;
+ char *str, *endp;
+
+ str = *argv;
+ abits = rbits = strtol(str, &endp, 0);
+ if (endp != str && *endp == ',') {
+ str = endp + 1;
+ abits = strtol(str, &endp, 0);
+ }
+ if (*endp != 0 || endp == str) {
+ option_error("invalid parameter '%s' for deflate option", *argv);
+ return 0;
+ }
+ if ((rbits != 0 && (rbits < DEFLATE_MIN_SIZE || rbits > DEFLATE_MAX_SIZE))
+ || (abits != 0 && (abits < DEFLATE_MIN_SIZE
+ || abits > DEFLATE_MAX_SIZE))) {
+ option_error("deflate option values must be 0 or %d .. %d",
+ DEFLATE_MIN_SIZE, DEFLATE_MAX_SIZE);
+ return 0;
+ }
+ if (rbits == DEFLATE_MIN_SIZE || abits == DEFLATE_MIN_SIZE) {
+ if (rbits == DEFLATE_MIN_SIZE)
+ rbits = DEFLATE_MIN_WORKS;
+ if (abits == DEFLATE_MIN_SIZE)
+ abits = DEFLATE_MIN_WORKS;
+ warn("deflate option value of %d changed to %d to avoid zlib bug",
+ DEFLATE_MIN_SIZE, DEFLATE_MIN_WORKS);
+ }
+ if (rbits > 0) {
+ ccp_wantoptions[0].deflate = 1;
+ ccp_wantoptions[0].deflate_size = rbits;
+ } else
+ ccp_wantoptions[0].deflate = 0;
+ if (abits > 0) {
+ ccp_allowoptions[0].deflate = 1;
+ ccp_allowoptions[0].deflate_size = abits;
+ } else
+ ccp_allowoptions[0].deflate = 0;
+ ppp_slprintf(deflate_value, sizeof(deflate_value),
+ rbits == abits? "%d": "%d,%d", rbits, abits);
+
+ return 1;
+}
+#endif /* PPP_OPTIONS */
+
+/*
+ * ccp_init - initialize CCP.
+ */
+static void ccp_init(ppp_pcb *pcb) {
+ fsm *f = &pcb->ccp_fsm;
+
+ f->pcb = pcb;
+ f->protocol = PPP_CCP;
+ f->callbacks = &ccp_callbacks;
+ fsm_init(f);
+
+#if 0 /* Not necessary, everything is cleared in ppp_new() */
+ memset(wo, 0, sizeof(*wo));
+ memset(go, 0, sizeof(*go));
+ memset(ao, 0, sizeof(*ao));
+ memset(ho, 0, sizeof(*ho));
+#endif /* 0 */
+
+#if DEFLATE_SUPPORT
+ wo->deflate = 1;
+ wo->deflate_size = DEFLATE_MAX_SIZE;
+ wo->deflate_correct = 1;
+ wo->deflate_draft = 1;
+ ao->deflate = 1;
+ ao->deflate_size = DEFLATE_MAX_SIZE;
+ ao->deflate_correct = 1;
+ ao->deflate_draft = 1;
+#endif /* DEFLATE_SUPPORT */
+
+#if BSDCOMPRESS_SUPPORT
+ wo->bsd_compress = 1;
+ wo->bsd_bits = BSD_MAX_BITS;
+ ao->bsd_compress = 1;
+ ao->bsd_bits = BSD_MAX_BITS;
+#endif /* BSDCOMPRESS_SUPPORT */
+
+#if PREDICTOR_SUPPORT
+ ao->predictor_1 = 1;
+#endif /* PREDICTOR_SUPPORT */
+}
+
+/*
+ * ccp_open - CCP is allowed to come up.
+ */
+static void ccp_open(ppp_pcb *pcb) {
+ fsm *f = &pcb->ccp_fsm;
+ ccp_options *go = &pcb->ccp_gotoptions;
+
+ if (f->state != PPP_FSM_OPENED)
+ ccp_set(pcb, 1, 0, 0, 0);
+
+ /*
+ * Find out which compressors the kernel supports before
+ * deciding whether to open in silent mode.
+ */
+ ccp_resetci(f);
+ if (!ccp_anycompress(go))
+ f->flags |= OPT_SILENT;
+
+ fsm_open(f);
+}
+
+/*
+ * ccp_close - Terminate CCP.
+ */
+static void ccp_close(ppp_pcb *pcb, const char *reason) {
+ fsm *f = &pcb->ccp_fsm;
+ ccp_set(pcb, 0, 0, 0, 0);
+ fsm_close(f, reason);
+}
+
+/*
+ * ccp_lowerup - we may now transmit CCP packets.
+ */
+static void ccp_lowerup(ppp_pcb *pcb) {
+ fsm *f = &pcb->ccp_fsm;
+ fsm_lowerup(f);
+}
+
+/*
+ * ccp_lowerdown - we may not transmit CCP packets.
+ */
+static void ccp_lowerdown(ppp_pcb *pcb) {
+ fsm *f = &pcb->ccp_fsm;
+ fsm_lowerdown(f);
+}
+
+/*
+ * ccp_input - process a received CCP packet.
+ */
+static void ccp_input(ppp_pcb *pcb, u_char *p, int len) {
+ fsm *f = &pcb->ccp_fsm;
+ ccp_options *go = &pcb->ccp_gotoptions;
+ int oldstate;
+
+ /*
+ * Check for a terminate-request so we can print a message.
+ */
+ oldstate = f->state;
+ fsm_input(f, p, len);
+ if (oldstate == PPP_FSM_OPENED && p[0] == TERMREQ && f->state != PPP_FSM_OPENED) {
+ ppp_notice("Compression disabled by peer.");
+#if MPPE_SUPPORT
+ if (go->mppe) {
+ ppp_error("MPPE disabled, closing LCP");
+ lcp_close(pcb, "MPPE disabled by peer");
+ }
+#endif /* MPPE_SUPPORT */
+ }
+
+ /*
+ * If we get a terminate-ack and we're not asking for compression,
+ * close CCP.
+ */
+ if (oldstate == PPP_FSM_REQSENT && p[0] == TERMACK
+ && !ccp_anycompress(go))
+ ccp_close(pcb, "No compression negotiated");
+}
+
+/*
+ * Handle a CCP-specific code.
+ */
+static int ccp_extcode(fsm *f, int code, int id, u_char *p, int len) {
+ ppp_pcb *pcb = f->pcb;
+ LWIP_UNUSED_ARG(p);
+ LWIP_UNUSED_ARG(len);
+
+ switch (code) {
+ case CCP_RESETREQ:
+ if (f->state != PPP_FSM_OPENED)
+ break;
+ ccp_reset_comp(pcb);
+ /* send a reset-ack, which the transmitter will see and
+ reset its compression state. */
+ fsm_sdata(f, CCP_RESETACK, id, NULL, 0);
+ break;
+
+ case CCP_RESETACK:
+ if ((pcb->ccp_localstate & RACK_PENDING) && id == f->reqid) {
+ pcb->ccp_localstate &= ~(RACK_PENDING | RREQ_REPEAT);
+ UNTIMEOUT(ccp_rack_timeout, f);
+ ccp_reset_decomp(pcb);
+ }
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ * ccp_protrej - peer doesn't talk CCP.
+ */
+static void ccp_protrej(ppp_pcb *pcb) {
+ fsm *f = &pcb->ccp_fsm;
+#if MPPE_SUPPORT
+ ccp_options *go = &pcb->ccp_gotoptions;
+#endif /* MPPE_SUPPORT */
+
+ ccp_set(pcb, 0, 0, 0, 0);
+ fsm_lowerdown(f);
+
+#if MPPE_SUPPORT
+ if (go->mppe) {
+ ppp_error("MPPE required but peer negotiation failed");
+ lcp_close(pcb, "MPPE required but peer negotiation failed");
+ }
+#endif /* MPPE_SUPPORT */
+
+}
+
+/*
+ * ccp_resetci - initialize at start of negotiation.
+ */
+static void ccp_resetci(fsm *f) {
+ ppp_pcb *pcb = f->pcb;
+ ccp_options *go = &pcb->ccp_gotoptions;
+ ccp_options *wo = &pcb->ccp_wantoptions;
+#if MPPE_SUPPORT
+ ccp_options *ao = &pcb->ccp_allowoptions;
+#endif /* MPPE_SUPPORT */
+#if DEFLATE_SUPPORT || BSDCOMPRESS_SUPPORT || PREDICTOR_SUPPORT
+ u_char opt_buf[CCP_MAX_OPTION_LENGTH];
+#endif /* DEFLATE_SUPPORT || BSDCOMPRESS_SUPPORT || PREDICTOR_SUPPORT */
+#if DEFLATE_SUPPORT || BSDCOMPRESS_SUPPORT
+ int res;
+#endif /* DEFLATE_SUPPORT || BSDCOMPRESS_SUPPORT */
+
+#if MPPE_SUPPORT
+ if (pcb->settings.require_mppe) {
+ wo->mppe = ao->mppe =
+ (pcb->settings.refuse_mppe_40 ? 0 : MPPE_OPT_40)
+ | (pcb->settings.refuse_mppe_128 ? 0 : MPPE_OPT_128);
+ }
+#endif /* MPPE_SUPPORT */
+
+ *go = *wo;
+ pcb->ccp_all_rejected = 0;
+
+#if MPPE_SUPPORT
+ if (go->mppe) {
+ int auth_mschap_bits = pcb->auth_done;
+ int numbits;
+
+ /*
+ * Start with a basic sanity check: mschap[v2] auth must be in
+ * exactly one direction. RFC 3079 says that the keys are
+ * 'derived from the credentials of the peer that initiated the call',
+ * however the PPP protocol doesn't have such a concept, and pppd
+ * cannot get this info externally. Instead we do the best we can.
+ * NB: If MPPE is required, all other compression opts are invalid.
+ * So, we return right away if we can't do it.
+ */
+
+ /* Leave only the mschap auth bits set */
+ auth_mschap_bits &= (CHAP_MS_WITHPEER | CHAP_MS_PEER |
+ CHAP_MS2_WITHPEER | CHAP_MS2_PEER);
+ /* Count the mschap auths */
+ auth_mschap_bits >>= CHAP_MS_SHIFT;
+ numbits = 0;
+ do {
+ numbits += auth_mschap_bits & 1;
+ auth_mschap_bits >>= 1;
+ } while (auth_mschap_bits);
+ if (numbits > 1) {
+ ppp_error("MPPE required, but auth done in both directions.");
+ lcp_close(pcb, "MPPE required but not available");
+ return;
+ }
+ if (!numbits) {
+ ppp_error("MPPE required, but MS-CHAP[v2] auth not performed.");
+ lcp_close(pcb, "MPPE required but not available");
+ return;
+ }
+
+ /* A plugin (eg radius) may not have obtained key material. */
+ if (!pcb->mppe_keys_set) {
+ ppp_error("MPPE required, but keys are not available. "
+ "Possible plugin problem?");
+ lcp_close(pcb, "MPPE required but not available");
+ return;
+ }
+
+ /* LM auth not supported for MPPE */
+ if (pcb->auth_done & (CHAP_MS_WITHPEER | CHAP_MS_PEER)) {
+ /* This might be noise */
+ if (go->mppe & MPPE_OPT_40) {
+ ppp_notice("Disabling 40-bit MPPE; MS-CHAP LM not supported");
+ go->mppe &= ~MPPE_OPT_40;
+ wo->mppe &= ~MPPE_OPT_40;
+ }
+ }
+
+ /* Last check: can we actually negotiate something? */
+ if (!(go->mppe & (MPPE_OPT_40 | MPPE_OPT_128))) {
+ /* Could be misconfig, could be 40-bit disabled above. */
+ ppp_error("MPPE required, but both 40-bit and 128-bit disabled.");
+ lcp_close(pcb, "MPPE required but not available");
+ return;
+ }
+
+ /* sync options */
+ ao->mppe = go->mppe;
+ /* MPPE is not compatible with other compression types */
+#if BSDCOMPRESS_SUPPORT
+ ao->bsd_compress = go->bsd_compress = 0;
+#endif /* BSDCOMPRESS_SUPPORT */
+#if PREDICTOR_SUPPORT
+ ao->predictor_1 = go->predictor_1 = 0;
+ ao->predictor_2 = go->predictor_2 = 0;
+#endif /* PREDICTOR_SUPPORT */
+#if DEFLATE_SUPPORT
+ ao->deflate = go->deflate = 0;
+#endif /* DEFLATE_SUPPORT */
+ }
+#endif /* MPPE_SUPPORT */
+
+ /*
+ * Check whether the kernel knows about the various
+ * compression methods we might request.
+ */
+#if BSDCOMPRESS_SUPPORT
+ /* FIXME: we don't need to test if BSD compress is available
+ * if BSDCOMPRESS_SUPPORT is set, it is.
+ */
+ if (go->bsd_compress) {
+ opt_buf[0] = CI_BSD_COMPRESS;
+ opt_buf[1] = CILEN_BSD_COMPRESS;
+ for (;;) {
+ if (go->bsd_bits < BSD_MIN_BITS) {
+ go->bsd_compress = 0;
+ break;
+ }
+ opt_buf[2] = BSD_MAKE_OPT(BSD_CURRENT_VERSION, go->bsd_bits);
+ res = ccp_test(pcb, opt_buf, CILEN_BSD_COMPRESS, 0);
+ if (res > 0) {
+ break;
+ } else if (res < 0) {
+ go->bsd_compress = 0;
+ break;
+ }
+ go->bsd_bits--;
+ }
+ }
+#endif /* BSDCOMPRESS_SUPPORT */
+#if DEFLATE_SUPPORT
+ /* FIXME: we don't need to test if deflate is available
+ * if DEFLATE_SUPPORT is set, it is.
+ */
+ if (go->deflate) {
+ if (go->deflate_correct) {
+ opt_buf[0] = CI_DEFLATE;
+ opt_buf[1] = CILEN_DEFLATE;
+ opt_buf[3] = DEFLATE_CHK_SEQUENCE;
+ for (;;) {
+ if (go->deflate_size < DEFLATE_MIN_WORKS) {
+ go->deflate_correct = 0;
+ break;
+ }
+ opt_buf[2] = DEFLATE_MAKE_OPT(go->deflate_size);
+ res = ccp_test(pcb, opt_buf, CILEN_DEFLATE, 0);
+ if (res > 0) {
+ break;
+ } else if (res < 0) {
+ go->deflate_correct = 0;
+ break;
+ }
+ go->deflate_size--;
+ }
+ }
+ if (go->deflate_draft) {
+ opt_buf[0] = CI_DEFLATE_DRAFT;
+ opt_buf[1] = CILEN_DEFLATE;
+ opt_buf[3] = DEFLATE_CHK_SEQUENCE;
+ for (;;) {
+ if (go->deflate_size < DEFLATE_MIN_WORKS) {
+ go->deflate_draft = 0;
+ break;
+ }
+ opt_buf[2] = DEFLATE_MAKE_OPT(go->deflate_size);
+ res = ccp_test(pcb, opt_buf, CILEN_DEFLATE, 0);
+ if (res > 0) {
+ break;
+ } else if (res < 0) {
+ go->deflate_draft = 0;
+ break;
+ }
+ go->deflate_size--;
+ }
+ }
+ if (!go->deflate_correct && !go->deflate_draft)
+ go->deflate = 0;
+ }
+#endif /* DEFLATE_SUPPORT */
+#if PREDICTOR_SUPPORT
+ /* FIXME: we don't need to test if predictor is available,
+ * if PREDICTOR_SUPPORT is set, it is.
+ */
+ if (go->predictor_1) {
+ opt_buf[0] = CI_PREDICTOR_1;
+ opt_buf[1] = CILEN_PREDICTOR_1;
+ if (ccp_test(pcb, opt_buf, CILEN_PREDICTOR_1, 0) <= 0)
+ go->predictor_1 = 0;
+ }
+ if (go->predictor_2) {
+ opt_buf[0] = CI_PREDICTOR_2;
+ opt_buf[1] = CILEN_PREDICTOR_2;
+ if (ccp_test(pcb, opt_buf, CILEN_PREDICTOR_2, 0) <= 0)
+ go->predictor_2 = 0;
+ }
+#endif /* PREDICTOR_SUPPORT */
+}
+
+/*
+ * ccp_cilen - Return total length of our configuration info.
+ */
+static int ccp_cilen(fsm *f) {
+ ppp_pcb *pcb = f->pcb;
+ ccp_options *go = &pcb->ccp_gotoptions;
+
+ return 0
+#if BSDCOMPRESS_SUPPORT
+ + (go->bsd_compress? CILEN_BSD_COMPRESS: 0)
+#endif /* BSDCOMPRESS_SUPPORT */
+#if DEFLATE_SUPPORT
+ + (go->deflate && go->deflate_correct? CILEN_DEFLATE: 0)
+ + (go->deflate && go->deflate_draft? CILEN_DEFLATE: 0)
+#endif /* DEFLATE_SUPPORT */
+#if PREDICTOR_SUPPORT
+ + (go->predictor_1? CILEN_PREDICTOR_1: 0)
+ + (go->predictor_2? CILEN_PREDICTOR_2: 0)
+#endif /* PREDICTOR_SUPPORT */
+#if MPPE_SUPPORT
+ + (go->mppe? CILEN_MPPE: 0)
+#endif /* MPPE_SUPPORT */
+ ;
+}
+
+/*
+ * ccp_addci - put our requests in a packet.
+ */
+static void ccp_addci(fsm *f, u_char *p, int *lenp) {
+ ppp_pcb *pcb = f->pcb;
+ ccp_options *go = &pcb->ccp_gotoptions;
+ u_char *p0 = p;
+
+ /*
+ * Add the compression types that we can receive, in decreasing
+ * preference order.
+ */
+#if MPPE_SUPPORT
+ if (go->mppe) {
+ p[0] = CI_MPPE;
+ p[1] = CILEN_MPPE;
+ MPPE_OPTS_TO_CI(go->mppe, &p[2]);
+ mppe_init(pcb, &pcb->mppe_decomp, go->mppe);
+ p += CILEN_MPPE;
+ }
+#endif /* MPPE_SUPPORT */
+#if DEFLATE_SUPPORT
+ if (go->deflate) {
+ if (go->deflate_correct) {
+ p[0] = CI_DEFLATE;
+ p[1] = CILEN_DEFLATE;
+ p[2] = DEFLATE_MAKE_OPT(go->deflate_size);
+ p[3] = DEFLATE_CHK_SEQUENCE;
+ p += CILEN_DEFLATE;
+ }
+ if (go->deflate_draft) {
+ p[0] = CI_DEFLATE_DRAFT;
+ p[1] = CILEN_DEFLATE;
+ p[2] = p[2 - CILEN_DEFLATE];
+ p[3] = DEFLATE_CHK_SEQUENCE;
+ p += CILEN_DEFLATE;
+ }
+ }
+#endif /* DEFLATE_SUPPORT */
+#if BSDCOMPRESS_SUPPORT
+ if (go->bsd_compress) {
+ p[0] = CI_BSD_COMPRESS;
+ p[1] = CILEN_BSD_COMPRESS;
+ p[2] = BSD_MAKE_OPT(BSD_CURRENT_VERSION, go->bsd_bits);
+ p += CILEN_BSD_COMPRESS;
+ }
+#endif /* BSDCOMPRESS_SUPPORT */
+#if PREDICTOR_SUPPORT
+ /* XXX Should Predictor 2 be preferable to Predictor 1? */
+ if (go->predictor_1) {
+ p[0] = CI_PREDICTOR_1;
+ p[1] = CILEN_PREDICTOR_1;
+ p += CILEN_PREDICTOR_1;
+ }
+ if (go->predictor_2) {
+ p[0] = CI_PREDICTOR_2;
+ p[1] = CILEN_PREDICTOR_2;
+ p += CILEN_PREDICTOR_2;
+ }
+#endif /* PREDICTOR_SUPPORT */
+
+ go->method = (p > p0)? p0[0]: 0;
+
+ *lenp = p - p0;
+}
+
+/*
+ * ccp_ackci - process a received configure-ack, and return
+ * 1 iff the packet was OK.
+ */
+static int ccp_ackci(fsm *f, u_char *p, int len) {
+ ppp_pcb *pcb = f->pcb;
+ ccp_options *go = &pcb->ccp_gotoptions;
+#if BSDCOMPRESS_SUPPORT || PREDICTOR_SUPPORT
+ u_char *p0 = p;
+#endif /* BSDCOMPRESS_SUPPORT || PREDICTOR_SUPPORT */
+
+#if MPPE_SUPPORT
+ if (go->mppe) {
+ u_char opt_buf[CILEN_MPPE];
+
+ opt_buf[0] = CI_MPPE;
+ opt_buf[1] = CILEN_MPPE;
+ MPPE_OPTS_TO_CI(go->mppe, &opt_buf[2]);
+ if (len < CILEN_MPPE || memcmp(opt_buf, p, CILEN_MPPE))
+ return 0;
+ p += CILEN_MPPE;
+ len -= CILEN_MPPE;
+ /* XXX Cope with first/fast ack */
+ if (len == 0)
+ return 1;
+ }
+#endif /* MPPE_SUPPORT */
+#if DEFLATE_SUPPORT
+ if (go->deflate) {
+ if (len < CILEN_DEFLATE
+ || p[0] != (go->deflate_correct? CI_DEFLATE: CI_DEFLATE_DRAFT)
+ || p[1] != CILEN_DEFLATE
+ || p[2] != DEFLATE_MAKE_OPT(go->deflate_size)
+ || p[3] != DEFLATE_CHK_SEQUENCE)
+ return 0;
+ p += CILEN_DEFLATE;
+ len -= CILEN_DEFLATE;
+ /* XXX Cope with first/fast ack */
+ if (len == 0)
+ return 1;
+ if (go->deflate_correct && go->deflate_draft) {
+ if (len < CILEN_DEFLATE
+ || p[0] != CI_DEFLATE_DRAFT
+ || p[1] != CILEN_DEFLATE
+ || p[2] != DEFLATE_MAKE_OPT(go->deflate_size)
+ || p[3] != DEFLATE_CHK_SEQUENCE)
+ return 0;
+ p += CILEN_DEFLATE;
+ len -= CILEN_DEFLATE;
+ }
+ }
+#endif /* DEFLATE_SUPPORT */
+#if BSDCOMPRESS_SUPPORT
+ if (go->bsd_compress) {
+ if (len < CILEN_BSD_COMPRESS
+ || p[0] != CI_BSD_COMPRESS || p[1] != CILEN_BSD_COMPRESS
+ || p[2] != BSD_MAKE_OPT(BSD_CURRENT_VERSION, go->bsd_bits))
+ return 0;
+ p += CILEN_BSD_COMPRESS;
+ len -= CILEN_BSD_COMPRESS;
+ /* XXX Cope with first/fast ack */
+ if (p == p0 && len == 0)
+ return 1;
+ }
+#endif /* BSDCOMPRESS_SUPPORT */
+#if PREDICTOR_SUPPORT
+ if (go->predictor_1) {
+ if (len < CILEN_PREDICTOR_1
+ || p[0] != CI_PREDICTOR_1 || p[1] != CILEN_PREDICTOR_1)
+ return 0;
+ p += CILEN_PREDICTOR_1;
+ len -= CILEN_PREDICTOR_1;
+ /* XXX Cope with first/fast ack */
+ if (p == p0 && len == 0)
+ return 1;
+ }
+ if (go->predictor_2) {
+ if (len < CILEN_PREDICTOR_2
+ || p[0] != CI_PREDICTOR_2 || p[1] != CILEN_PREDICTOR_2)
+ return 0;
+ p += CILEN_PREDICTOR_2;
+ len -= CILEN_PREDICTOR_2;
+ /* XXX Cope with first/fast ack */
+ if (p == p0 && len == 0)
+ return 1;
+ }
+#endif /* PREDICTOR_SUPPORT */
+
+ if (len != 0)
+ return 0;
+ return 1;
+}
+
+/*
+ * ccp_nakci - process received configure-nak.
+ * Returns 1 iff the nak was OK.
+ */
+static int ccp_nakci(fsm *f, u_char *p, int len, int treat_as_reject) {
+ ppp_pcb *pcb = f->pcb;
+ ccp_options *go = &pcb->ccp_gotoptions;
+ ccp_options no; /* options we've seen already */
+ ccp_options try_; /* options to ask for next time */
+ LWIP_UNUSED_ARG(treat_as_reject);
+#if !MPPE_SUPPORT && !DEFLATE_SUPPORT && !BSDCOMPRESS_SUPPORT
+ LWIP_UNUSED_ARG(p);
+ LWIP_UNUSED_ARG(len);
+#endif /* !MPPE_SUPPORT && !DEFLATE_SUPPORT && !BSDCOMPRESS_SUPPORT */
+
+ memset(&no, 0, sizeof(no));
+ try_ = *go;
+
+#if MPPE_SUPPORT
+ if (go->mppe && len >= CILEN_MPPE
+ && p[0] == CI_MPPE && p[1] == CILEN_MPPE) {
+ no.mppe = 1;
+ /*
+ * Peer wants us to use a different strength or other setting.
+ * Fail if we aren't willing to use his suggestion.
+ */
+ MPPE_CI_TO_OPTS(&p[2], try_.mppe);
+ if ((try_.mppe & MPPE_OPT_STATEFUL) && pcb->settings.refuse_mppe_stateful) {
+ ppp_error("Refusing MPPE stateful mode offered by peer");
+ try_.mppe = 0;
+ } else if (((go->mppe | MPPE_OPT_STATEFUL) & try_.mppe) != try_.mppe) {
+ /* Peer must have set options we didn't request (suggest) */
+ try_.mppe = 0;
+ }
+
+ if (!try_.mppe) {
+ ppp_error("MPPE required but peer negotiation failed");
+ lcp_close(pcb, "MPPE required but peer negotiation failed");
+ }
+ }
+#endif /* MPPE_SUPPORT */
+#if DEFLATE_SUPPORT
+ if (go->deflate && len >= CILEN_DEFLATE
+ && p[0] == (go->deflate_correct? CI_DEFLATE: CI_DEFLATE_DRAFT)
+ && p[1] == CILEN_DEFLATE) {
+ no.deflate = 1;
+ /*
+ * Peer wants us to use a different code size or something.
+ * Stop asking for Deflate if we don't understand his suggestion.
+ */
+ if (DEFLATE_METHOD(p[2]) != DEFLATE_METHOD_VAL
+ || DEFLATE_SIZE(p[2]) < DEFLATE_MIN_WORKS
+ || p[3] != DEFLATE_CHK_SEQUENCE)
+ try_.deflate = 0;
+ else if (DEFLATE_SIZE(p[2]) < go->deflate_size)
+ try_.deflate_size = DEFLATE_SIZE(p[2]);
+ p += CILEN_DEFLATE;
+ len -= CILEN_DEFLATE;
+ if (go->deflate_correct && go->deflate_draft
+ && len >= CILEN_DEFLATE && p[0] == CI_DEFLATE_DRAFT
+ && p[1] == CILEN_DEFLATE) {
+ p += CILEN_DEFLATE;
+ len -= CILEN_DEFLATE;
+ }
+ }
+#endif /* DEFLATE_SUPPORT */
+#if BSDCOMPRESS_SUPPORT
+ if (go->bsd_compress && len >= CILEN_BSD_COMPRESS
+ && p[0] == CI_BSD_COMPRESS && p[1] == CILEN_BSD_COMPRESS) {
+ no.bsd_compress = 1;
+ /*
+ * Peer wants us to use a different number of bits
+ * or a different version.
+ */
+ if (BSD_VERSION(p[2]) != BSD_CURRENT_VERSION)
+ try_.bsd_compress = 0;
+ else if (BSD_NBITS(p[2]) < go->bsd_bits)
+ try_.bsd_bits = BSD_NBITS(p[2]);
+ p += CILEN_BSD_COMPRESS;
+ len -= CILEN_BSD_COMPRESS;
+ }
+#endif /* BSDCOMPRESS_SUPPORT */
+
+ /*
+ * Predictor-1 and 2 have no options, so they can't be Naked.
+ *
+ * There may be remaining options but we ignore them.
+ */
+
+ if (f->state != PPP_FSM_OPENED)
+ *go = try_;
+ return 1;
+}
+
+/*
+ * ccp_rejci - reject some of our suggested compression methods.
+ */
+static int ccp_rejci(fsm *f, u_char *p, int len) {
+ ppp_pcb *pcb = f->pcb;
+ ccp_options *go = &pcb->ccp_gotoptions;
+ ccp_options try_; /* options to request next time */
+
+ try_ = *go;
+
+ /*
+ * Cope with empty configure-rejects by ceasing to send
+ * configure-requests.
+ */
+ if (len == 0 && pcb->ccp_all_rejected)
+ return -1;
+
+#if MPPE_SUPPORT
+ if (go->mppe && len >= CILEN_MPPE
+ && p[0] == CI_MPPE && p[1] == CILEN_MPPE) {
+ ppp_error("MPPE required but peer refused");
+ lcp_close(pcb, "MPPE required but peer refused");
+ p += CILEN_MPPE;
+ len -= CILEN_MPPE;
+ }
+#endif /* MPPE_SUPPORT */
+#if DEFLATE_SUPPORT
+ if (go->deflate_correct && len >= CILEN_DEFLATE
+ && p[0] == CI_DEFLATE && p[1] == CILEN_DEFLATE) {
+ if (p[2] != DEFLATE_MAKE_OPT(go->deflate_size)
+ || p[3] != DEFLATE_CHK_SEQUENCE)
+ return 0; /* Rej is bad */
+ try_.deflate_correct = 0;
+ p += CILEN_DEFLATE;
+ len -= CILEN_DEFLATE;
+ }
+ if (go->deflate_draft && len >= CILEN_DEFLATE
+ && p[0] == CI_DEFLATE_DRAFT && p[1] == CILEN_DEFLATE) {
+ if (p[2] != DEFLATE_MAKE_OPT(go->deflate_size)
+ || p[3] != DEFLATE_CHK_SEQUENCE)
+ return 0; /* Rej is bad */
+ try_.deflate_draft = 0;
+ p += CILEN_DEFLATE;
+ len -= CILEN_DEFLATE;
+ }
+ if (!try_.deflate_correct && !try_.deflate_draft)
+ try_.deflate = 0;
+#endif /* DEFLATE_SUPPORT */
+#if BSDCOMPRESS_SUPPORT
+ if (go->bsd_compress && len >= CILEN_BSD_COMPRESS
+ && p[0] == CI_BSD_COMPRESS && p[1] == CILEN_BSD_COMPRESS) {
+ if (p[2] != BSD_MAKE_OPT(BSD_CURRENT_VERSION, go->bsd_bits))
+ return 0;
+ try_.bsd_compress = 0;
+ p += CILEN_BSD_COMPRESS;
+ len -= CILEN_BSD_COMPRESS;
+ }
+#endif /* BSDCOMPRESS_SUPPORT */
+#if PREDICTOR_SUPPORT
+ if (go->predictor_1 && len >= CILEN_PREDICTOR_1
+ && p[0] == CI_PREDICTOR_1 && p[1] == CILEN_PREDICTOR_1) {
+ try_.predictor_1 = 0;
+ p += CILEN_PREDICTOR_1;
+ len -= CILEN_PREDICTOR_1;
+ }
+ if (go->predictor_2 && len >= CILEN_PREDICTOR_2
+ && p[0] == CI_PREDICTOR_2 && p[1] == CILEN_PREDICTOR_2) {
+ try_.predictor_2 = 0;
+ p += CILEN_PREDICTOR_2;
+ len -= CILEN_PREDICTOR_2;
+ }
+#endif /* PREDICTOR_SUPPORT */
+
+ if (len != 0)
+ return 0;
+
+ if (f->state != PPP_FSM_OPENED)
+ *go = try_;
+
+ return 1;
+}
+
+/*
+ * ccp_reqci - processed a received configure-request.
+ * Returns CONFACK, CONFNAK or CONFREJ and the packet modified
+ * appropriately.
+ */
+static int ccp_reqci(fsm *f, u_char *p, int *lenp, int dont_nak) {
+ ppp_pcb *pcb = f->pcb;
+ ccp_options *ho = &pcb->ccp_hisoptions;
+ ccp_options *ao = &pcb->ccp_allowoptions;
+ int ret, newret;
+#if DEFLATE_SUPPORT || BSDCOMPRESS_SUPPORT
+ int res;
+ int nb;
+#endif /* DEFLATE_SUPPORT || BSDCOMPRESS_SUPPORT */
+ u_char *p0, *retp;
+ int len, clen, type;
+#if MPPE_SUPPORT
+ u8_t rej_for_ci_mppe = 1; /* Are we rejecting based on a bad/missing */
+ /* CI_MPPE, or due to other options? */
+#endif /* MPPE_SUPPORT */
+
+ ret = CONFACK;
+ retp = p0 = p;
+ len = *lenp;
+
+ memset(ho, 0, sizeof(ccp_options));
+ ho->method = (len > 0)? p[0]: 0;
+
+ while (len > 0) {
+ newret = CONFACK;
+ if (len < 2 || p[1] < 2 || p[1] > len) {
+ /* length is bad */
+ clen = len;
+ newret = CONFREJ;
+
+ } else {
+ type = p[0];
+ clen = p[1];
+
+ switch (type) {
+#if MPPE_SUPPORT
+ case CI_MPPE:
+ if (!ao->mppe || clen != CILEN_MPPE) {
+ newret = CONFREJ;
+ break;
+ }
+ MPPE_CI_TO_OPTS(&p[2], ho->mppe);
+
+ /* Nak if anything unsupported or unknown are set. */
+ if (ho->mppe & MPPE_OPT_UNSUPPORTED) {
+ newret = CONFNAK;
+ ho->mppe &= ~MPPE_OPT_UNSUPPORTED;
+ }
+ if (ho->mppe & MPPE_OPT_UNKNOWN) {
+ newret = CONFNAK;
+ ho->mppe &= ~MPPE_OPT_UNKNOWN;
+ }
+
+ /* Check state opt */
+ if (ho->mppe & MPPE_OPT_STATEFUL) {
+ /*
+ * We can Nak and request stateless, but it's a
+ * lot easier to just assume the peer will request
+ * it if he can do it; stateful mode is bad over
+ * the Internet -- which is where we expect MPPE.
+ */
+ if (pcb->settings.refuse_mppe_stateful) {
+ ppp_error("Refusing MPPE stateful mode offered by peer");
+ newret = CONFREJ;
+ break;
+ }
+ }
+
+ /* Find out which of {S,L} are set. */
+ if ((ho->mppe & MPPE_OPT_128)
+ && (ho->mppe & MPPE_OPT_40)) {
+ /* Both are set, negotiate the strongest. */
+ newret = CONFNAK;
+ if (ao->mppe & MPPE_OPT_128)
+ ho->mppe &= ~MPPE_OPT_40;
+ else if (ao->mppe & MPPE_OPT_40)
+ ho->mppe &= ~MPPE_OPT_128;
+ else {
+ newret = CONFREJ;
+ break;
+ }
+ } else if (ho->mppe & MPPE_OPT_128) {
+ if (!(ao->mppe & MPPE_OPT_128)) {
+ newret = CONFREJ;
+ break;
+ }
+ } else if (ho->mppe & MPPE_OPT_40) {
+ if (!(ao->mppe & MPPE_OPT_40)) {
+ newret = CONFREJ;
+ break;
+ }
+ } else {
+ /* Neither are set. */
+ /* We cannot accept this. */
+ newret = CONFNAK;
+ /* Give the peer our idea of what can be used,
+ so it can choose and confirm */
+ ho->mppe = ao->mppe;
+ }
+
+ /* rebuild the opts */
+ MPPE_OPTS_TO_CI(ho->mppe, &p[2]);
+ if (newret == CONFACK) {
+ int mtu;
+
+ mppe_init(pcb, &pcb->mppe_comp, ho->mppe);
+ /*
+ * We need to decrease the interface MTU by MPPE_PAD
+ * because MPPE frames **grow**. The kernel [must]
+ * allocate MPPE_PAD extra bytes in xmit buffers.
+ */
+ mtu = netif_get_mtu(pcb);
+ if (mtu)
+ netif_set_mtu(pcb, mtu - MPPE_PAD);
+ else
+ newret = CONFREJ;
+ }
+
+ /*
+ * We have accepted MPPE or are willing to negotiate
+ * MPPE parameters. A CONFREJ is due to subsequent
+ * (non-MPPE) processing.
+ */
+ rej_for_ci_mppe = 0;
+ break;
+#endif /* MPPE_SUPPORT */
+#if DEFLATE_SUPPORT
+ case CI_DEFLATE:
+ case CI_DEFLATE_DRAFT:
+ if (!ao->deflate || clen != CILEN_DEFLATE
+ || (!ao->deflate_correct && type == CI_DEFLATE)
+ || (!ao->deflate_draft && type == CI_DEFLATE_DRAFT)) {
+ newret = CONFREJ;
+ break;
+ }
+
+ ho->deflate = 1;
+ ho->deflate_size = nb = DEFLATE_SIZE(p[2]);
+ if (DEFLATE_METHOD(p[2]) != DEFLATE_METHOD_VAL
+ || p[3] != DEFLATE_CHK_SEQUENCE
+ || nb > ao->deflate_size || nb < DEFLATE_MIN_WORKS) {
+ newret = CONFNAK;
+ if (!dont_nak) {
+ p[2] = DEFLATE_MAKE_OPT(ao->deflate_size);
+ p[3] = DEFLATE_CHK_SEQUENCE;
+ /* fall through to test this #bits below */
+ } else
+ break;
+ }
+
+ /*
+ * Check whether we can do Deflate with the window
+ * size they want. If the window is too big, reduce
+ * it until the kernel can cope and nak with that.
+ * We only check this for the first option.
+ */
+ if (p == p0) {
+ for (;;) {
+ res = ccp_test(pcb, p, CILEN_DEFLATE, 1);
+ if (res > 0)
+ break; /* it's OK now */
+ if (res < 0 || nb == DEFLATE_MIN_WORKS || dont_nak) {
+ newret = CONFREJ;
+ p[2] = DEFLATE_MAKE_OPT(ho->deflate_size);
+ break;
+ }
+ newret = CONFNAK;
+ --nb;
+ p[2] = DEFLATE_MAKE_OPT(nb);
+ }
+ }
+ break;
+#endif /* DEFLATE_SUPPORT */
+#if BSDCOMPRESS_SUPPORT
+ case CI_BSD_COMPRESS:
+ if (!ao->bsd_compress || clen != CILEN_BSD_COMPRESS) {
+ newret = CONFREJ;
+ break;
+ }
+
+ ho->bsd_compress = 1;
+ ho->bsd_bits = nb = BSD_NBITS(p[2]);
+ if (BSD_VERSION(p[2]) != BSD_CURRENT_VERSION
+ || nb > ao->bsd_bits || nb < BSD_MIN_BITS) {
+ newret = CONFNAK;
+ if (!dont_nak) {
+ p[2] = BSD_MAKE_OPT(BSD_CURRENT_VERSION, ao->bsd_bits);
+ /* fall through to test this #bits below */
+ } else
+ break;
+ }
+
+ /*
+ * Check whether we can do BSD-Compress with the code
+ * size they want. If the code size is too big, reduce
+ * it until the kernel can cope and nak with that.
+ * We only check this for the first option.
+ */
+ if (p == p0) {
+ for (;;) {
+ res = ccp_test(pcb, p, CILEN_BSD_COMPRESS, 1);
+ if (res > 0)
+ break;
+ if (res < 0 || nb == BSD_MIN_BITS || dont_nak) {
+ newret = CONFREJ;
+ p[2] = BSD_MAKE_OPT(BSD_CURRENT_VERSION,
+ ho->bsd_bits);
+ break;
+ }
+ newret = CONFNAK;
+ --nb;
+ p[2] = BSD_MAKE_OPT(BSD_CURRENT_VERSION, nb);
+ }
+ }
+ break;
+#endif /* BSDCOMPRESS_SUPPORT */
+#if PREDICTOR_SUPPORT
+ case CI_PREDICTOR_1:
+ if (!ao->predictor_1 || clen != CILEN_PREDICTOR_1) {
+ newret = CONFREJ;
+ break;
+ }
+
+ ho->predictor_1 = 1;
+ if (p == p0
+ && ccp_test(pcb, p, CILEN_PREDICTOR_1, 1) <= 0) {
+ newret = CONFREJ;
+ }
+ break;
+
+ case CI_PREDICTOR_2:
+ if (!ao->predictor_2 || clen != CILEN_PREDICTOR_2) {
+ newret = CONFREJ;
+ break;
+ }
+
+ ho->predictor_2 = 1;
+ if (p == p0
+ && ccp_test(pcb, p, CILEN_PREDICTOR_2, 1) <= 0) {
+ newret = CONFREJ;
+ }
+ break;
+#endif /* PREDICTOR_SUPPORT */
+
+ default:
+ newret = CONFREJ;
+ }
+ }
+
+ if (newret == CONFNAK && dont_nak)
+ newret = CONFREJ;
+ if (!(newret == CONFACK || (newret == CONFNAK && ret == CONFREJ))) {
+ /* we're returning this option */
+ if (newret == CONFREJ && ret == CONFNAK)
+ retp = p0;
+ ret = newret;
+ if (p != retp)
+ MEMCPY(retp, p, clen);
+ retp += clen;
+ }
+
+ p += clen;
+ len -= clen;
+ }
+
+ if (ret != CONFACK) {
+ if (ret == CONFREJ && *lenp == retp - p0)
+ pcb->ccp_all_rejected = 1;
+ else
+ *lenp = retp - p0;
+ }
+#if MPPE_SUPPORT
+ if (ret == CONFREJ && ao->mppe && rej_for_ci_mppe) {
+ ppp_error("MPPE required but peer negotiation failed");
+ lcp_close(pcb, "MPPE required but peer negotiation failed");
+ }
+#endif /* MPPE_SUPPORT */
+ return ret;
+}
+
+/*
+ * Make a string name for a compression method (or 2).
+ */
+static const char *method_name(ccp_options *opt, ccp_options *opt2) {
+ static char result[64];
+#if !DEFLATE_SUPPORT && !BSDCOMPRESS_SUPPORT
+ LWIP_UNUSED_ARG(opt2);
+#endif /* !DEFLATE_SUPPORT && !BSDCOMPRESS_SUPPORT */
+
+ if (!ccp_anycompress(opt))
+ return "(none)";
+ switch (opt->method) {
+#if MPPE_SUPPORT
+ case CI_MPPE:
+ {
+ char *p = result;
+ char *q = result + sizeof(result); /* 1 past result */
+
+ ppp_slprintf(p, q - p, "MPPE ");
+ p += 5;
+ if (opt->mppe & MPPE_OPT_128) {
+ ppp_slprintf(p, q - p, "128-bit ");
+ p += 8;
+ }
+ if (opt->mppe & MPPE_OPT_40) {
+ ppp_slprintf(p, q - p, "40-bit ");
+ p += 7;
+ }
+ if (opt->mppe & MPPE_OPT_STATEFUL)
+ ppp_slprintf(p, q - p, "stateful");
+ else
+ ppp_slprintf(p, q - p, "stateless");
+
+ break;
+ }
+#endif /* MPPE_SUPPORT */
+#if DEFLATE_SUPPORT
+ case CI_DEFLATE:
+ case CI_DEFLATE_DRAFT:
+ if (opt2 != NULL && opt2->deflate_size != opt->deflate_size)
+ ppp_slprintf(result, sizeof(result), "Deflate%s (%d/%d)",
+ (opt->method == CI_DEFLATE_DRAFT? "(old#)": ""),
+ opt->deflate_size, opt2->deflate_size);
+ else
+ ppp_slprintf(result, sizeof(result), "Deflate%s (%d)",
+ (opt->method == CI_DEFLATE_DRAFT? "(old#)": ""),
+ opt->deflate_size);
+ break;
+#endif /* DEFLATE_SUPPORT */
+#if BSDCOMPRESS_SUPPORT
+ case CI_BSD_COMPRESS:
+ if (opt2 != NULL && opt2->bsd_bits != opt->bsd_bits)
+ ppp_slprintf(result, sizeof(result), "BSD-Compress (%d/%d)",
+ opt->bsd_bits, opt2->bsd_bits);
+ else
+ ppp_slprintf(result, sizeof(result), "BSD-Compress (%d)",
+ opt->bsd_bits);
+ break;
+#endif /* BSDCOMPRESS_SUPPORT */
+#if PREDICTOR_SUPPORT
+ case CI_PREDICTOR_1:
+ return "Predictor 1";
+ case CI_PREDICTOR_2:
+ return "Predictor 2";
+#endif /* PREDICTOR_SUPPORT */
+ default:
+ ppp_slprintf(result, sizeof(result), "Method %d", opt->method);
+ }
+ return result;
+}
+
+/*
+ * CCP has come up - inform the kernel driver and log a message.
+ */
+static void ccp_up(fsm *f) {
+ ppp_pcb *pcb = f->pcb;
+ ccp_options *go = &pcb->ccp_gotoptions;
+ ccp_options *ho = &pcb->ccp_hisoptions;
+ char method1[64];
+
+ ccp_set(pcb, 1, 1, go->method, ho->method);
+ if (ccp_anycompress(go)) {
+ if (ccp_anycompress(ho)) {
+ if (go->method == ho->method) {
+ ppp_notice("%s compression enabled", method_name(go, ho));
+ } else {
+ ppp_strlcpy(method1, method_name(go, NULL), sizeof(method1));
+ ppp_notice("%s / %s compression enabled",
+ method1, method_name(ho, NULL));
+ }
+ } else
+ ppp_notice("%s receive compression enabled", method_name(go, NULL));
+ } else if (ccp_anycompress(ho))
+ ppp_notice("%s transmit compression enabled", method_name(ho, NULL));
+#if MPPE_SUPPORT
+ if (go->mppe) {
+ continue_networks(pcb); /* Bring up IP et al */
+ }
+#endif /* MPPE_SUPPORT */
+}
+
+/*
+ * CCP has gone down - inform the kernel driver.
+ */
+static void ccp_down(fsm *f) {
+ ppp_pcb *pcb = f->pcb;
+#if MPPE_SUPPORT
+ ccp_options *go = &pcb->ccp_gotoptions;
+#endif /* MPPE_SUPPORT */
+
+ if (pcb->ccp_localstate & RACK_PENDING)
+ UNTIMEOUT(ccp_rack_timeout, f);
+ pcb->ccp_localstate = 0;
+ ccp_set(pcb, 1, 0, 0, 0);
+#if MPPE_SUPPORT
+ if (go->mppe) {
+ go->mppe = 0;
+ if (pcb->lcp_fsm.state == PPP_FSM_OPENED) {
+ /* If LCP is not already going down, make sure it does. */
+ ppp_error("MPPE disabled");
+ lcp_close(pcb, "MPPE disabled");
+ }
+ }
+#endif /* MPPE_SUPPORT */
+}
+
+#if PRINTPKT_SUPPORT
+/*
+ * Print the contents of a CCP packet.
+ */
+static const char* const ccp_codenames[] = {
+ "ConfReq", "ConfAck", "ConfNak", "ConfRej",
+ "TermReq", "TermAck", "CodeRej",
+ NULL, NULL, NULL, NULL, NULL, NULL,
+ "ResetReq", "ResetAck",
+};
+
+static int ccp_printpkt(const u_char *p, int plen, void (*printer) (void *, const char *, ...), void *arg) {
+ const u_char *p0, *optend;
+ int code, id, len;
+ int optlen;
+
+ p0 = p;
+ if (plen < HEADERLEN)
+ return 0;
+ code = p[0];
+ id = p[1];
+ len = (p[2] << 8) + p[3];
+ if (len < HEADERLEN || len > plen)
+ return 0;
+
+ if (code >= 1 && code <= (int)LWIP_ARRAYSIZE(ccp_codenames) && ccp_codenames[code-1] != NULL)
+ printer(arg, " %s", ccp_codenames[code-1]);
+ else
+ printer(arg, " code=0x%x", code);
+ printer(arg, " id=0x%x", id);
+ len -= HEADERLEN;
+ p += HEADERLEN;
+
+ switch (code) {
+ case CONFREQ:
+ case CONFACK:
+ case CONFNAK:
+ case CONFREJ:
+ /* print list of possible compression methods */
+ while (len >= 2) {
+ code = p[0];
+ optlen = p[1];
+ if (optlen < 2 || optlen > len)
+ break;
+ printer(arg, " <");
+ len -= optlen;
+ optend = p + optlen;
+ switch (code) {
+#if MPPE_SUPPORT
+ case CI_MPPE:
+ if (optlen >= CILEN_MPPE) {
+ u_char mppe_opts;
+
+ MPPE_CI_TO_OPTS(&p[2], mppe_opts);
+ printer(arg, "mppe %s %s %s %s %s %s%s",
+ (p[2] & MPPE_H_BIT)? "+H": "-H",
+ (p[5] & MPPE_M_BIT)? "+M": "-M",
+ (p[5] & MPPE_S_BIT)? "+S": "-S",
+ (p[5] & MPPE_L_BIT)? "+L": "-L",
+ (p[5] & MPPE_D_BIT)? "+D": "-D",
+ (p[5] & MPPE_C_BIT)? "+C": "-C",
+ (mppe_opts & MPPE_OPT_UNKNOWN)? " +U": "");
+ if (mppe_opts & MPPE_OPT_UNKNOWN)
+ printer(arg, " (%.2x %.2x %.2x %.2x)",
+ p[2], p[3], p[4], p[5]);
+ p += CILEN_MPPE;
+ }
+ break;
+#endif /* MPPE_SUPPORT */
+#if DEFLATE_SUPPORT
+ case CI_DEFLATE:
+ case CI_DEFLATE_DRAFT:
+ if (optlen >= CILEN_DEFLATE) {
+ printer(arg, "deflate%s %d",
+ (code == CI_DEFLATE_DRAFT? "(old#)": ""),
+ DEFLATE_SIZE(p[2]));
+ if (DEFLATE_METHOD(p[2]) != DEFLATE_METHOD_VAL)
+ printer(arg, " method %d", DEFLATE_METHOD(p[2]));
+ if (p[3] != DEFLATE_CHK_SEQUENCE)
+ printer(arg, " check %d", p[3]);
+ p += CILEN_DEFLATE;
+ }
+ break;
+#endif /* DEFLATE_SUPPORT */
+#if BSDCOMPRESS_SUPPORT
+ case CI_BSD_COMPRESS:
+ if (optlen >= CILEN_BSD_COMPRESS) {
+ printer(arg, "bsd v%d %d", BSD_VERSION(p[2]),
+ BSD_NBITS(p[2]));
+ p += CILEN_BSD_COMPRESS;
+ }
+ break;
+#endif /* BSDCOMPRESS_SUPPORT */
+#if PREDICTOR_SUPPORT
+ case CI_PREDICTOR_1:
+ if (optlen >= CILEN_PREDICTOR_1) {
+ printer(arg, "predictor 1");
+ p += CILEN_PREDICTOR_1;
+ }
+ break;
+ case CI_PREDICTOR_2:
+ if (optlen >= CILEN_PREDICTOR_2) {
+ printer(arg, "predictor 2");
+ p += CILEN_PREDICTOR_2;
+ }
+ break;
+#endif /* PREDICTOR_SUPPORT */
+ default:
+ break;
+ }
+ while (p < optend)
+ printer(arg, " %.2x", *p++);
+ printer(arg, ">");
+ }
+ break;
+
+ case TERMACK:
+ case TERMREQ:
+ if (len > 0 && *p >= ' ' && *p < 0x7f) {
+ ppp_print_string(p, len, printer, arg);
+ p += len;
+ len = 0;
+ }
+ break;
+ default:
+ break;
+ }
+
+ /* dump out the rest of the packet in hex */
+ while (--len >= 0)
+ printer(arg, " %.2x", *p++);
+
+ return p - p0;
+}
+#endif /* PRINTPKT_SUPPORT */
+
+#if PPP_DATAINPUT
+/*
+ * We have received a packet that the decompressor failed to
+ * decompress. Here we would expect to issue a reset-request, but
+ * Motorola has a patent on resetting the compressor as a result of
+ * detecting an error in the decompressed data after decompression.
+ * (See US patent 5,130,993; international patent publication number
+ * WO 91/10289; Australian patent 73296/91.)
+ *
+ * So we ask the kernel whether the error was detected after
+ * decompression; if it was, we take CCP down, thus disabling
+ * compression :-(, otherwise we issue the reset-request.
+ */
+static void ccp_datainput(ppp_pcb *pcb, u_char *pkt, int len) {
+ fsm *f;
+#if MPPE_SUPPORT
+ ccp_options *go = &pcb->ccp_gotoptions;
+#endif /* MPPE_SUPPORT */
+ LWIP_UNUSED_ARG(pkt);
+ LWIP_UNUSED_ARG(len);
+
+ f = &pcb->ccp_fsm;
+ if (f->state == PPP_FSM_OPENED) {
+ if (ccp_fatal_error(pcb)) {
+ /*
+ * Disable compression by taking CCP down.
+ */
+ ppp_error("Lost compression sync: disabling compression");
+ ccp_close(pcb, "Lost compression sync");
+#if MPPE_SUPPORT
+ /*
+ * If we were doing MPPE, we must also take the link down.
+ */
+ if (go->mppe) {
+ ppp_error("Too many MPPE errors, closing LCP");
+ lcp_close(pcb, "Too many MPPE errors");
+ }
+#endif /* MPPE_SUPPORT */
+ } else {
+ /*
+ * Send a reset-request to reset the peer's compressor.
+ * We don't do that if we are still waiting for an
+ * acknowledgement to a previous reset-request.
+ */
+ if (!(pcb->ccp_localstate & RACK_PENDING)) {
+ fsm_sdata(f, CCP_RESETREQ, f->reqid = ++f->id, NULL, 0);
+ TIMEOUT(ccp_rack_timeout, f, RACKTIMEOUT);
+ pcb->ccp_localstate |= RACK_PENDING;
+ } else
+ pcb->ccp_localstate |= RREQ_REPEAT;
+ }
+ }
+}
+#endif /* PPP_DATAINPUT */
+
+/*
+ * We have received a packet that the decompressor failed to
+ * decompress. Issue a reset-request.
+ */
+void ccp_resetrequest(ppp_pcb *pcb) {
+ fsm *f = &pcb->ccp_fsm;
+
+ if (f->state != PPP_FSM_OPENED)
+ return;
+
+ /*
+ * Send a reset-request to reset the peer's compressor.
+ * We don't do that if we are still waiting for an
+ * acknowledgement to a previous reset-request.
+ */
+ if (!(pcb->ccp_localstate & RACK_PENDING)) {
+ fsm_sdata(f, CCP_RESETREQ, f->reqid = ++f->id, NULL, 0);
+ TIMEOUT(ccp_rack_timeout, f, RACKTIMEOUT);
+ pcb->ccp_localstate |= RACK_PENDING;
+ } else
+ pcb->ccp_localstate |= RREQ_REPEAT;
+}
+
+/*
+ * Timeout waiting for reset-ack.
+ */
+static void ccp_rack_timeout(void *arg) {
+ fsm *f = (fsm*)arg;
+ ppp_pcb *pcb = f->pcb;
+
+ if (f->state == PPP_FSM_OPENED && (pcb->ccp_localstate & RREQ_REPEAT)) {
+ fsm_sdata(f, CCP_RESETREQ, f->reqid, NULL, 0);
+ TIMEOUT(ccp_rack_timeout, f, RACKTIMEOUT);
+ pcb->ccp_localstate &= ~RREQ_REPEAT;
+ } else
+ pcb->ccp_localstate &= ~RACK_PENDING;
+}
+
+#endif /* PPP_SUPPORT && CCP_SUPPORT */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/chap-md5.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/chap-md5.c
new file mode 100644
index 0000000..88f069f
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/chap-md5.c
@@ -0,0 +1,126 @@
+/*
+ * chap-md5.c - New CHAP/MD5 implementation.
+ *
+ * Copyright (c) 2003 Paul Mackerras. 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. The name(s) of the authors of this software must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission.
+ *
+ * 3. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by Paul Mackerras
+ * <paulus@samba.org>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT && CHAP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#if 0 /* UNUSED */
+#include <stdlib.h>
+#include <string.h>
+#endif /* UNUSED */
+
+#include "netif/ppp/ppp_impl.h"
+
+#include "netif/ppp/chap-new.h"
+#include "netif/ppp/chap-md5.h"
+#include "netif/ppp/magic.h"
+#include "netif/ppp/pppcrypt.h"
+
+#define MD5_HASH_SIZE 16
+#define MD5_MIN_CHALLENGE 17
+#define MD5_MAX_CHALLENGE 24
+#define MD5_MIN_MAX_POWER_OF_TWO_CHALLENGE 3 /* 2^3-1 = 7, 17+7 = 24 */
+
+#if PPP_SERVER
+static void chap_md5_generate_challenge(ppp_pcb *pcb, unsigned char *cp) {
+ int clen;
+ LWIP_UNUSED_ARG(pcb);
+
+ clen = MD5_MIN_CHALLENGE + magic_pow(MD5_MIN_MAX_POWER_OF_TWO_CHALLENGE);
+ *cp++ = clen;
+ magic_random_bytes(cp, clen);
+}
+
+static int chap_md5_verify_response(ppp_pcb *pcb, int id, const char *name,
+ const unsigned char *secret, int secret_len,
+ const unsigned char *challenge, const unsigned char *response,
+ char *message, int message_space) {
+ lwip_md5_context ctx;
+ unsigned char idbyte = id;
+ unsigned char hash[MD5_HASH_SIZE];
+ int challenge_len, response_len;
+ LWIP_UNUSED_ARG(name);
+ LWIP_UNUSED_ARG(pcb);
+
+ challenge_len = *challenge++;
+ response_len = *response++;
+ if (response_len == MD5_HASH_SIZE) {
+ /* Generate hash of ID, secret, challenge */
+ lwip_md5_init(&ctx);
+ lwip_md5_starts(&ctx);
+ lwip_md5_update(&ctx, &idbyte, 1);
+ lwip_md5_update(&ctx, secret, secret_len);
+ lwip_md5_update(&ctx, challenge, challenge_len);
+ lwip_md5_finish(&ctx, hash);
+ lwip_md5_free(&ctx);
+
+ /* Test if our hash matches the peer's response */
+ if (memcmp(hash, response, MD5_HASH_SIZE) == 0) {
+ ppp_slprintf(message, message_space, "Access granted");
+ return 1;
+ }
+ }
+ ppp_slprintf(message, message_space, "Access denied");
+ return 0;
+}
+#endif /* PPP_SERVER */
+
+static void chap_md5_make_response(ppp_pcb *pcb, unsigned char *response, int id, const char *our_name,
+ const unsigned char *challenge, const char *secret, int secret_len,
+ unsigned char *private_) {
+ lwip_md5_context ctx;
+ unsigned char idbyte = id;
+ int challenge_len = *challenge++;
+ LWIP_UNUSED_ARG(our_name);
+ LWIP_UNUSED_ARG(private_);
+ LWIP_UNUSED_ARG(pcb);
+
+ lwip_md5_init(&ctx);
+ lwip_md5_starts(&ctx);
+ lwip_md5_update(&ctx, &idbyte, 1);
+ lwip_md5_update(&ctx, (const u_char *)secret, secret_len);
+ lwip_md5_update(&ctx, challenge, challenge_len);
+ lwip_md5_finish(&ctx, &response[1]);
+ lwip_md5_free(&ctx);
+ response[0] = MD5_HASH_SIZE;
+}
+
+const struct chap_digest_type md5_digest = {
+ CHAP_MD5, /* code */
+#if PPP_SERVER
+ chap_md5_generate_challenge,
+ chap_md5_verify_response,
+#endif /* PPP_SERVER */
+ chap_md5_make_response,
+ NULL, /* check_success */
+ NULL, /* handle_failure */
+};
+
+#endif /* PPP_SUPPORT && CHAP_SUPPORT */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/chap-new.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/chap-new.c
new file mode 100644
index 0000000..485122d
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/chap-new.c
@@ -0,0 +1,677 @@
+/*
+ * chap-new.c - New CHAP implementation.
+ *
+ * Copyright (c) 2003 Paul Mackerras. 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. The name(s) of the authors of this software must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission.
+ *
+ * 3. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by Paul Mackerras
+ * <paulus@samba.org>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT && CHAP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#if 0 /* UNUSED */
+#include <stdlib.h>
+#include <string.h>
+#endif /* UNUSED */
+
+#include "netif/ppp/ppp_impl.h"
+
+#if 0 /* UNUSED */
+#include "session.h"
+#endif /* UNUSED */
+
+#include "netif/ppp/chap-new.h"
+#include "netif/ppp/chap-md5.h"
+#if MSCHAP_SUPPORT
+#include "netif/ppp/chap_ms.h"
+#endif
+#include "netif/ppp/magic.h"
+
+#if 0 /* UNUSED */
+/* Hook for a plugin to validate CHAP challenge */
+int (*chap_verify_hook)(const char *name, const char *ourname, int id,
+ const struct chap_digest_type *digest,
+ const unsigned char *challenge, const unsigned char *response,
+ char *message, int message_space) = NULL;
+#endif /* UNUSED */
+
+#if PPP_OPTIONS
+/*
+ * Command-line options.
+ */
+static option_t chap_option_list[] = {
+ { "chap-restart", o_int, &chap_timeout_time,
+ "Set timeout for CHAP", OPT_PRIO },
+ { "chap-max-challenge", o_int, &pcb->settings.chap_max_transmits,
+ "Set max #xmits for challenge", OPT_PRIO },
+ { "chap-interval", o_int, &pcb->settings.chap_rechallenge_time,
+ "Set interval for rechallenge", OPT_PRIO },
+ { NULL }
+};
+#endif /* PPP_OPTIONS */
+
+
+/* Values for flags in chap_client_state and chap_server_state */
+#define LOWERUP 1
+#define AUTH_STARTED 2
+#define AUTH_DONE 4
+#define AUTH_FAILED 8
+#define TIMEOUT_PENDING 0x10
+#define CHALLENGE_VALID 0x20
+
+/*
+ * Prototypes.
+ */
+static void chap_init(ppp_pcb *pcb);
+static void chap_lowerup(ppp_pcb *pcb);
+static void chap_lowerdown(ppp_pcb *pcb);
+#if PPP_SERVER
+static void chap_timeout(void *arg);
+static void chap_generate_challenge(ppp_pcb *pcb);
+static void chap_handle_response(ppp_pcb *pcb, int code,
+ unsigned char *pkt, int len);
+static int chap_verify_response(ppp_pcb *pcb, const char *name, const char *ourname, int id,
+ const struct chap_digest_type *digest,
+ const unsigned char *challenge, const unsigned char *response,
+ char *message, int message_space);
+#endif /* PPP_SERVER */
+static void chap_respond(ppp_pcb *pcb, int id,
+ unsigned char *pkt, int len);
+static void chap_handle_status(ppp_pcb *pcb, int code, int id,
+ unsigned char *pkt, int len);
+static void chap_protrej(ppp_pcb *pcb);
+static void chap_input(ppp_pcb *pcb, unsigned char *pkt, int pktlen);
+#if PRINTPKT_SUPPORT
+static int chap_print_pkt(const unsigned char *p, int plen,
+ void (*printer) (void *, const char *, ...), void *arg);
+#endif /* PRINTPKT_SUPPORT */
+
+/* List of digest types that we know about */
+static const struct chap_digest_type* const chap_digests[] = {
+ &md5_digest,
+#if MSCHAP_SUPPORT
+ &chapms_digest,
+ &chapms2_digest,
+#endif /* MSCHAP_SUPPORT */
+ NULL
+};
+
+/*
+ * chap_init - reset to initial state.
+ */
+static void chap_init(ppp_pcb *pcb) {
+ LWIP_UNUSED_ARG(pcb);
+
+#if 0 /* Not necessary, everything is cleared in ppp_new() */
+ memset(&pcb->chap_client, 0, sizeof(chap_client_state));
+#if PPP_SERVER
+ memset(&pcb->chap_server, 0, sizeof(chap_server_state));
+#endif /* PPP_SERVER */
+#endif /* 0 */
+}
+
+/*
+ * chap_lowerup - we can start doing stuff now.
+ */
+static void chap_lowerup(ppp_pcb *pcb) {
+
+ pcb->chap_client.flags |= LOWERUP;
+#if PPP_SERVER
+ pcb->chap_server.flags |= LOWERUP;
+ if (pcb->chap_server.flags & AUTH_STARTED)
+ chap_timeout(pcb);
+#endif /* PPP_SERVER */
+}
+
+static void chap_lowerdown(ppp_pcb *pcb) {
+
+ pcb->chap_client.flags = 0;
+#if PPP_SERVER
+ if (pcb->chap_server.flags & TIMEOUT_PENDING)
+ UNTIMEOUT(chap_timeout, pcb);
+ pcb->chap_server.flags = 0;
+#endif /* PPP_SERVER */
+}
+
+#if PPP_SERVER
+/*
+ * chap_auth_peer - Start authenticating the peer.
+ * If the lower layer is already up, we start sending challenges,
+ * otherwise we wait for the lower layer to come up.
+ */
+void chap_auth_peer(ppp_pcb *pcb, const char *our_name, int digest_code) {
+ const struct chap_digest_type *dp;
+ int i;
+
+ if (pcb->chap_server.flags & AUTH_STARTED) {
+ ppp_error("CHAP: peer authentication already started!");
+ return;
+ }
+ for (i = 0; (dp = chap_digests[i]) != NULL; ++i)
+ if (dp->code == digest_code)
+ break;
+ if (dp == NULL)
+ ppp_fatal("CHAP digest 0x%x requested but not available",
+ digest_code);
+
+ pcb->chap_server.digest = dp;
+ pcb->chap_server.name = our_name;
+ /* Start with a random ID value */
+ pcb->chap_server.id = magic();
+ pcb->chap_server.flags |= AUTH_STARTED;
+ if (pcb->chap_server.flags & LOWERUP)
+ chap_timeout(pcb);
+}
+#endif /* PPP_SERVER */
+
+/*
+ * chap_auth_with_peer - Prepare to authenticate ourselves to the peer.
+ * There isn't much to do until we receive a challenge.
+ */
+void chap_auth_with_peer(ppp_pcb *pcb, const char *our_name, int digest_code) {
+ const struct chap_digest_type *dp;
+ int i;
+
+ if(NULL == our_name)
+ return;
+
+ if (pcb->chap_client.flags & AUTH_STARTED) {
+ ppp_error("CHAP: authentication with peer already started!");
+ return;
+ }
+ for (i = 0; (dp = chap_digests[i]) != NULL; ++i)
+ if (dp->code == digest_code)
+ break;
+
+ if (dp == NULL)
+ ppp_fatal("CHAP digest 0x%x requested but not available",
+ digest_code);
+
+ pcb->chap_client.digest = dp;
+ pcb->chap_client.name = our_name;
+ pcb->chap_client.flags |= AUTH_STARTED;
+}
+
+#if PPP_SERVER
+/*
+ * chap_timeout - It's time to send another challenge to the peer.
+ * This could be either a retransmission of a previous challenge,
+ * or a new challenge to start re-authentication.
+ */
+static void chap_timeout(void *arg) {
+ ppp_pcb *pcb = (ppp_pcb*)arg;
+ struct pbuf *p;
+
+ pcb->chap_server.flags &= ~TIMEOUT_PENDING;
+ if ((pcb->chap_server.flags & CHALLENGE_VALID) == 0) {
+ pcb->chap_server.challenge_xmits = 0;
+ chap_generate_challenge(pcb);
+ pcb->chap_server.flags |= CHALLENGE_VALID;
+ } else if (pcb->chap_server.challenge_xmits >= pcb->settings.chap_max_transmits) {
+ pcb->chap_server.flags &= ~CHALLENGE_VALID;
+ pcb->chap_server.flags |= AUTH_DONE | AUTH_FAILED;
+ auth_peer_fail(pcb, PPP_CHAP);
+ return;
+ }
+
+ p = pbuf_alloc(PBUF_RAW, (u16_t)(pcb->chap_server.challenge_pktlen), PPP_CTRL_PBUF_TYPE);
+ if(NULL == p)
+ return;
+ if(p->tot_len != p->len) {
+ pbuf_free(p);
+ return;
+ }
+ MEMCPY(p->payload, pcb->chap_server.challenge, pcb->chap_server.challenge_pktlen);
+ ppp_write(pcb, p);
+ ++pcb->chap_server.challenge_xmits;
+ pcb->chap_server.flags |= TIMEOUT_PENDING;
+ TIMEOUT(chap_timeout, arg, pcb->settings.chap_timeout_time);
+}
+
+/*
+ * chap_generate_challenge - generate a challenge string and format
+ * the challenge packet in pcb->chap_server.challenge_pkt.
+ */
+static void chap_generate_challenge(ppp_pcb *pcb) {
+ int clen = 1, nlen, len;
+ unsigned char *p;
+
+ p = pcb->chap_server.challenge;
+ MAKEHEADER(p, PPP_CHAP);
+ p += CHAP_HDRLEN;
+ pcb->chap_server.digest->generate_challenge(pcb, p);
+ clen = *p;
+ nlen = strlen(pcb->chap_server.name);
+ memcpy(p + 1 + clen, pcb->chap_server.name, nlen);
+
+ len = CHAP_HDRLEN + 1 + clen + nlen;
+ pcb->chap_server.challenge_pktlen = PPP_HDRLEN + len;
+
+ p = pcb->chap_server.challenge + PPP_HDRLEN;
+ p[0] = CHAP_CHALLENGE;
+ p[1] = ++pcb->chap_server.id;
+ p[2] = len >> 8;
+ p[3] = len;
+}
+
+/*
+ * chap_handle_response - check the response to our challenge.
+ */
+static void chap_handle_response(ppp_pcb *pcb, int id,
+ unsigned char *pkt, int len) {
+ int response_len, ok, mlen;
+ const unsigned char *response;
+ unsigned char *outp;
+ struct pbuf *p;
+ const char *name = NULL; /* initialized to shut gcc up */
+#if 0 /* UNUSED */
+ int (*verifier)(const char *, const char *, int, const struct chap_digest_type *,
+ const unsigned char *, const unsigned char *, char *, int);
+#endif /* UNUSED */
+ char rname[MAXNAMELEN+1];
+ char message[256];
+
+ if ((pcb->chap_server.flags & LOWERUP) == 0)
+ return;
+ if (id != pcb->chap_server.challenge[PPP_HDRLEN+1] || len < 2)
+ return;
+ if (pcb->chap_server.flags & CHALLENGE_VALID) {
+ response = pkt;
+ GETCHAR(response_len, pkt);
+ len -= response_len + 1; /* length of name */
+ name = (char *)pkt + response_len;
+ if (len < 0)
+ return;
+
+ if (pcb->chap_server.flags & TIMEOUT_PENDING) {
+ pcb->chap_server.flags &= ~TIMEOUT_PENDING;
+ UNTIMEOUT(chap_timeout, pcb);
+ }
+#if PPP_REMOTENAME
+ if (pcb->settings.explicit_remote) {
+ name = pcb->remote_name;
+ } else
+#endif /* PPP_REMOTENAME */
+ {
+ /* Null terminate and clean remote name. */
+ ppp_slprintf(rname, sizeof(rname), "%.*v", len, name);
+ name = rname;
+ }
+
+#if 0 /* UNUSED */
+ if (chap_verify_hook)
+ verifier = chap_verify_hook;
+ else
+ verifier = chap_verify_response;
+ ok = (*verifier)(name, pcb->chap_server.name, id, pcb->chap_server.digest,
+ pcb->chap_server.challenge + PPP_HDRLEN + CHAP_HDRLEN,
+ response, pcb->chap_server.message, sizeof(pcb->chap_server.message));
+#endif /* UNUSED */
+ ok = chap_verify_response(pcb, name, pcb->chap_server.name, id, pcb->chap_server.digest,
+ pcb->chap_server.challenge + PPP_HDRLEN + CHAP_HDRLEN,
+ response, message, sizeof(message));
+#if 0 /* UNUSED */
+ if (!ok || !auth_number()) {
+#endif /* UNUSED */
+ if (!ok) {
+ pcb->chap_server.flags |= AUTH_FAILED;
+ ppp_warn("Peer %q failed CHAP authentication", name);
+ }
+ } else if ((pcb->chap_server.flags & AUTH_DONE) == 0)
+ return;
+
+ /* send the response */
+ mlen = strlen(message);
+ len = CHAP_HDRLEN + mlen;
+ p = pbuf_alloc(PBUF_RAW, (u16_t)(PPP_HDRLEN +len), PPP_CTRL_PBUF_TYPE);
+ if(NULL == p)
+ return;
+ if(p->tot_len != p->len) {
+ pbuf_free(p);
+ return;
+ }
+
+ outp = (unsigned char *)p->payload;
+ MAKEHEADER(outp, PPP_CHAP);
+
+ outp[0] = (pcb->chap_server.flags & AUTH_FAILED)? CHAP_FAILURE: CHAP_SUCCESS;
+ outp[1] = id;
+ outp[2] = len >> 8;
+ outp[3] = len;
+ if (mlen > 0)
+ memcpy(outp + CHAP_HDRLEN, message, mlen);
+ ppp_write(pcb, p);
+
+ if (pcb->chap_server.flags & CHALLENGE_VALID) {
+ pcb->chap_server.flags &= ~CHALLENGE_VALID;
+ if (!(pcb->chap_server.flags & AUTH_DONE) && !(pcb->chap_server.flags & AUTH_FAILED)) {
+
+#if 0 /* UNUSED */
+ /*
+ * Auth is OK, so now we need to check session restrictions
+ * to ensure everything is OK, but only if we used a
+ * plugin, and only if we're configured to check. This
+ * allows us to do PAM checks on PPP servers that
+ * authenticate against ActiveDirectory, and use AD for
+ * account info (like when using Winbind integrated with
+ * PAM).
+ */
+ if (session_mgmt &&
+ session_check(name, NULL, devnam, NULL) == 0) {
+ pcb->chap_server.flags |= AUTH_FAILED;
+ ppp_warn("Peer %q failed CHAP Session verification", name);
+ }
+#endif /* UNUSED */
+
+ }
+ if (pcb->chap_server.flags & AUTH_FAILED) {
+ auth_peer_fail(pcb, PPP_CHAP);
+ } else {
+ if ((pcb->chap_server.flags & AUTH_DONE) == 0)
+ auth_peer_success(pcb, PPP_CHAP,
+ pcb->chap_server.digest->code,
+ name, strlen(name));
+ if (pcb->settings.chap_rechallenge_time) {
+ pcb->chap_server.flags |= TIMEOUT_PENDING;
+ TIMEOUT(chap_timeout, pcb,
+ pcb->settings.chap_rechallenge_time);
+ }
+ }
+ pcb->chap_server.flags |= AUTH_DONE;
+ }
+}
+
+/*
+ * chap_verify_response - check whether the peer's response matches
+ * what we think it should be. Returns 1 if it does (authentication
+ * succeeded), or 0 if it doesn't.
+ */
+static int chap_verify_response(ppp_pcb *pcb, const char *name, const char *ourname, int id,
+ const struct chap_digest_type *digest,
+ const unsigned char *challenge, const unsigned char *response,
+ char *message, int message_space) {
+ int ok;
+ unsigned char secret[MAXSECRETLEN];
+ int secret_len;
+
+ /* Get the secret that the peer is supposed to know */
+ if (!get_secret(pcb, name, ourname, (char *)secret, &secret_len, 1)) {
+ ppp_error("No CHAP secret found for authenticating %q", name);
+ return 0;
+ }
+ ok = digest->verify_response(pcb, id, name, secret, secret_len, challenge,
+ response, message, message_space);
+ memset(secret, 0, sizeof(secret));
+
+ return ok;
+}
+#endif /* PPP_SERVER */
+
+/*
+ * chap_respond - Generate and send a response to a challenge.
+ */
+static void chap_respond(ppp_pcb *pcb, int id,
+ unsigned char *pkt, int len) {
+ int clen, nlen;
+ int secret_len;
+ struct pbuf *p;
+ u_char *outp;
+ char rname[MAXNAMELEN+1];
+ char secret[MAXSECRETLEN+1];
+
+ p = pbuf_alloc(PBUF_RAW, (u16_t)(RESP_MAX_PKTLEN), PPP_CTRL_PBUF_TYPE);
+ if(NULL == p)
+ return;
+ if(p->tot_len != p->len) {
+ pbuf_free(p);
+ return;
+ }
+
+ if ((pcb->chap_client.flags & (LOWERUP | AUTH_STARTED)) != (LOWERUP | AUTH_STARTED))
+ return; /* not ready */
+ if (len < 2 || len < pkt[0] + 1)
+ return; /* too short */
+ clen = pkt[0];
+ nlen = len - (clen + 1);
+
+ /* Null terminate and clean remote name. */
+ ppp_slprintf(rname, sizeof(rname), "%.*v", nlen, pkt + clen + 1);
+
+#if PPP_REMOTENAME
+ /* Microsoft doesn't send their name back in the PPP packet */
+ if (pcb->settings.explicit_remote || (pcb->settings.remote_name[0] != 0 && rname[0] == 0))
+ strlcpy(rname, pcb->settings.remote_name, sizeof(rname));
+#endif /* PPP_REMOTENAME */
+
+ /* get secret for authenticating ourselves with the specified host */
+ if (!get_secret(pcb, pcb->chap_client.name, rname, secret, &secret_len, 0)) {
+ secret_len = 0; /* assume null secret if can't find one */
+ ppp_warn("No CHAP secret found for authenticating us to %q", rname);
+ }
+
+ outp = (u_char*)p->payload;
+ MAKEHEADER(outp, PPP_CHAP);
+ outp += CHAP_HDRLEN;
+
+ pcb->chap_client.digest->make_response(pcb, outp, id, pcb->chap_client.name, pkt,
+ secret, secret_len, pcb->chap_client.priv);
+ memset(secret, 0, secret_len);
+
+ clen = *outp;
+ nlen = strlen(pcb->chap_client.name);
+ memcpy(outp + clen + 1, pcb->chap_client.name, nlen);
+
+ outp = (u_char*)p->payload + PPP_HDRLEN;
+ len = CHAP_HDRLEN + clen + 1 + nlen;
+ outp[0] = CHAP_RESPONSE;
+ outp[1] = id;
+ outp[2] = len >> 8;
+ outp[3] = len;
+
+ pbuf_realloc(p, PPP_HDRLEN + len);
+ ppp_write(pcb, p);
+}
+
+static void chap_handle_status(ppp_pcb *pcb, int code, int id,
+ unsigned char *pkt, int len) {
+ const char *msg = NULL;
+ LWIP_UNUSED_ARG(id);
+
+ if ((pcb->chap_client.flags & (AUTH_DONE|AUTH_STARTED|LOWERUP))
+ != (AUTH_STARTED|LOWERUP))
+ return;
+ pcb->chap_client.flags |= AUTH_DONE;
+
+ if (code == CHAP_SUCCESS) {
+ /* used for MS-CHAP v2 mutual auth, yuck */
+ if (pcb->chap_client.digest->check_success != NULL) {
+ if (!(*pcb->chap_client.digest->check_success)(pcb, pkt, len, pcb->chap_client.priv))
+ code = CHAP_FAILURE;
+ } else
+ msg = "CHAP authentication succeeded";
+ } else {
+ if (pcb->chap_client.digest->handle_failure != NULL)
+ (*pcb->chap_client.digest->handle_failure)(pcb, pkt, len);
+ else
+ msg = "CHAP authentication failed";
+ }
+ if (msg) {
+ if (len > 0)
+ ppp_info("%s: %.*v", msg, len, pkt);
+ else
+ ppp_info("%s", msg);
+ }
+ if (code == CHAP_SUCCESS)
+ auth_withpeer_success(pcb, PPP_CHAP, pcb->chap_client.digest->code);
+ else {
+ pcb->chap_client.flags |= AUTH_FAILED;
+ ppp_error("CHAP authentication failed");
+ auth_withpeer_fail(pcb, PPP_CHAP);
+ }
+}
+
+static void chap_input(ppp_pcb *pcb, unsigned char *pkt, int pktlen) {
+ unsigned char code, id;
+ int len;
+
+ if (pktlen < CHAP_HDRLEN)
+ return;
+ GETCHAR(code, pkt);
+ GETCHAR(id, pkt);
+ GETSHORT(len, pkt);
+ if (len < CHAP_HDRLEN || len > pktlen)
+ return;
+ len -= CHAP_HDRLEN;
+
+ switch (code) {
+ case CHAP_CHALLENGE:
+ chap_respond(pcb, id, pkt, len);
+ break;
+#if PPP_SERVER
+ case CHAP_RESPONSE:
+ chap_handle_response(pcb, id, pkt, len);
+ break;
+#endif /* PPP_SERVER */
+ case CHAP_FAILURE:
+ case CHAP_SUCCESS:
+ chap_handle_status(pcb, code, id, pkt, len);
+ break;
+ default:
+ break;
+ }
+}
+
+static void chap_protrej(ppp_pcb *pcb) {
+
+#if PPP_SERVER
+ if (pcb->chap_server.flags & TIMEOUT_PENDING) {
+ pcb->chap_server.flags &= ~TIMEOUT_PENDING;
+ UNTIMEOUT(chap_timeout, pcb);
+ }
+ if (pcb->chap_server.flags & AUTH_STARTED) {
+ pcb->chap_server.flags = 0;
+ auth_peer_fail(pcb, PPP_CHAP);
+ }
+#endif /* PPP_SERVER */
+ if ((pcb->chap_client.flags & (AUTH_STARTED|AUTH_DONE)) == AUTH_STARTED) {
+ pcb->chap_client.flags &= ~AUTH_STARTED;
+ ppp_error("CHAP authentication failed due to protocol-reject");
+ auth_withpeer_fail(pcb, PPP_CHAP);
+ }
+}
+
+#if PRINTPKT_SUPPORT
+/*
+ * chap_print_pkt - print the contents of a CHAP packet.
+ */
+static const char* const chap_code_names[] = {
+ "Challenge", "Response", "Success", "Failure"
+};
+
+static int chap_print_pkt(const unsigned char *p, int plen,
+ void (*printer) (void *, const char *, ...), void *arg) {
+ int code, id, len;
+ int clen, nlen;
+ unsigned char x;
+
+ if (plen < CHAP_HDRLEN)
+ return 0;
+ GETCHAR(code, p);
+ GETCHAR(id, p);
+ GETSHORT(len, p);
+ if (len < CHAP_HDRLEN || len > plen)
+ return 0;
+
+ if (code >= 1 && code <= (int)LWIP_ARRAYSIZE(chap_code_names))
+ printer(arg, " %s", chap_code_names[code-1]);
+ else
+ printer(arg, " code=0x%x", code);
+ printer(arg, " id=0x%x", id);
+ len -= CHAP_HDRLEN;
+ 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 = ");
+ ppp_print_string(p, nlen, printer, arg);
+ break;
+ case CHAP_FAILURE:
+ case CHAP_SUCCESS:
+ printer(arg, " ");
+ ppp_print_string(p, len, printer, arg);
+ break;
+ default:
+ for (clen = len; clen > 0; --clen) {
+ GETCHAR(x, p);
+ printer(arg, " %.2x", x);
+ }
+ /* no break */
+ }
+
+ return len + CHAP_HDRLEN;
+}
+#endif /* PRINTPKT_SUPPORT */
+
+const struct protent chap_protent = {
+ PPP_CHAP,
+ chap_init,
+ chap_input,
+ chap_protrej,
+ chap_lowerup,
+ chap_lowerdown,
+ NULL, /* open */
+ NULL, /* close */
+#if PRINTPKT_SUPPORT
+ chap_print_pkt,
+#endif /* PRINTPKT_SUPPORT */
+#if PPP_DATAINPUT
+ NULL, /* datainput */
+#endif /* PPP_DATAINPUT */
+#if PRINTPKT_SUPPORT
+ "CHAP", /* name */
+ NULL, /* data_name */
+#endif /* PRINTPKT_SUPPORT */
+#if PPP_OPTIONS
+ chap_option_list,
+ NULL, /* check_options */
+#endif /* PPP_OPTIONS */
+#if DEMAND_SUPPORT
+ NULL,
+ NULL
+#endif /* DEMAND_SUPPORT */
+};
+
+#endif /* PPP_SUPPORT && CHAP_SUPPORT */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/chap_ms.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/chap_ms.c
new file mode 100644
index 0000000..5a989c9
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/chap_ms.c
@@ -0,0 +1,962 @@
+/*
+ * chap_ms.c - Microsoft MS-CHAP compatible implementation.
+ *
+ * Copyright (c) 1995 Eric Rosenquist. 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(s) of the authors of this software must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission.
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * 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
+ */
+
+/*
+ * Modifications by Frank Cusack, frank@google.com, March 2002.
+ *
+ * Implemented MS-CHAPv2 functionality, heavily based on sample
+ * implementation in RFC 2759. Implemented MPPE functionality,
+ * heavily based on sample implementation in RFC 3079.
+ *
+ * Copyright (c) 2002 Google, Inc. 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(s) of the authors of this software must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission.
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT && MSCHAP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#if 0 /* UNUSED */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <unistd.h>
+#endif /* UNUSED */
+
+#include "netif/ppp/ppp_impl.h"
+
+#include "netif/ppp/chap-new.h"
+#include "netif/ppp/chap_ms.h"
+#include "netif/ppp/pppcrypt.h"
+#include "netif/ppp/magic.h"
+#if MPPE_SUPPORT
+#include "netif/ppp/mppe.h" /* For mppe_sha1_pad*, mppe_set_key() */
+#endif /* MPPE_SUPPORT */
+
+#define SHA1_SIGNATURE_SIZE 20
+#define MD4_SIGNATURE_SIZE 16 /* 16 bytes in a MD4 message digest */
+#define MAX_NT_PASSWORD 256 /* Max (Unicode) chars in an NT pass */
+
+#define MS_CHAP_RESPONSE_LEN 49 /* Response length for MS-CHAP */
+#define MS_CHAP2_RESPONSE_LEN 49 /* Response length for MS-CHAPv2 */
+#define MS_AUTH_RESPONSE_LENGTH 40 /* MS-CHAPv2 authenticator response, */
+ /* as ASCII */
+
+/* Error codes for MS-CHAP failure messages. */
+#define MS_CHAP_ERROR_RESTRICTED_LOGON_HOURS 646
+#define MS_CHAP_ERROR_ACCT_DISABLED 647
+#define MS_CHAP_ERROR_PASSWD_EXPIRED 648
+#define MS_CHAP_ERROR_NO_DIALIN_PERMISSION 649
+#define MS_CHAP_ERROR_AUTHENTICATION_FAILURE 691
+#define MS_CHAP_ERROR_CHANGING_PASSWORD 709
+
+/*
+ * Offsets within the response field for MS-CHAP
+ */
+#define MS_CHAP_LANMANRESP 0
+#define MS_CHAP_LANMANRESP_LEN 24
+#define MS_CHAP_NTRESP 24
+#define MS_CHAP_NTRESP_LEN 24
+#define MS_CHAP_USENT 48
+
+/*
+ * Offsets within the response field for MS-CHAP2
+ */
+#define MS_CHAP2_PEER_CHALLENGE 0
+#define MS_CHAP2_PEER_CHAL_LEN 16
+#define MS_CHAP2_RESERVED_LEN 8
+#define MS_CHAP2_NTRESP 24
+#define MS_CHAP2_NTRESP_LEN 24
+#define MS_CHAP2_FLAGS 48
+
+#if MPPE_SUPPORT
+#if 0 /* UNUSED */
+/* These values are the RADIUS attribute values--see RFC 2548. */
+#define MPPE_ENC_POL_ENC_ALLOWED 1
+#define MPPE_ENC_POL_ENC_REQUIRED 2
+#define MPPE_ENC_TYPES_RC4_40 2
+#define MPPE_ENC_TYPES_RC4_128 4
+
+/* used by plugins (using above values) */
+extern void set_mppe_enc_types(int, int);
+#endif /* UNUSED */
+#endif /* MPPE_SUPPORT */
+
+/* Are we the authenticator or authenticatee? For MS-CHAPv2 key derivation. */
+#define MS_CHAP2_AUTHENTICATEE 0
+#define MS_CHAP2_AUTHENTICATOR 1
+
+static void ascii2unicode (const char[], int, u_char[]);
+static void NTPasswordHash (u_char *, int, u_char[MD4_SIGNATURE_SIZE]);
+static void ChallengeResponse (const u_char *, const u_char *, u_char[24]);
+static void ChallengeHash (const u_char[16], const u_char *, const char *, u_char[8]);
+static void ChapMS_NT (const u_char *, const char *, int, u_char[24]);
+static void ChapMS2_NT (const u_char *, const u_char[16], const char *, const char *, int,
+ u_char[24]);
+static void GenerateAuthenticatorResponsePlain
+ (const char*, int, u_char[24], const u_char[16], const u_char *,
+ const char *, u_char[41]);
+#ifdef MSLANMAN
+static void ChapMS_LANMan (u_char *, char *, int, u_char *);
+#endif
+
+static void GenerateAuthenticatorResponse(const u_char PasswordHashHash[MD4_SIGNATURE_SIZE],
+ u_char NTResponse[24], const u_char PeerChallenge[16],
+ const u_char *rchallenge, const char *username,
+ u_char authResponse[MS_AUTH_RESPONSE_LENGTH+1]);
+
+#if MPPE_SUPPORT
+static void Set_Start_Key (ppp_pcb *pcb, const u_char *, const char *, int);
+static void SetMasterKeys (ppp_pcb *pcb, const char *, int, u_char[24], int);
+#endif /* MPPE_SUPPORT */
+
+static void ChapMS (ppp_pcb *pcb, const u_char *, const char *, int, u_char *);
+static void ChapMS2 (ppp_pcb *pcb, const u_char *, const u_char *, const char *, const char *, int,
+ u_char *, u_char[MS_AUTH_RESPONSE_LENGTH+1], int);
+
+#ifdef MSLANMAN
+bool ms_lanman = 0; /* Use LanMan password instead of NT */
+ /* Has meaning only with MS-CHAP challenges */
+#endif
+
+#if MPPE_SUPPORT
+#ifdef DEBUGMPPEKEY
+/* For MPPE debug */
+/* Use "[]|}{?/><,`!2&&(" (sans quotes) for RFC 3079 MS-CHAPv2 test value */
+static char *mschap_challenge = NULL;
+/* Use "!@\#$%^&*()_+:3|~" (sans quotes, backslash is to escape #) for ... */
+static char *mschap2_peer_challenge = NULL;
+#endif
+
+#include "netif/ppp/fsm.h" /* Need to poke MPPE options */
+#include "netif/ppp/ccp.h"
+#endif /* MPPE_SUPPORT */
+
+#if PPP_OPTIONS
+/*
+ * Command-line options.
+ */
+static option_t chapms_option_list[] = {
+#ifdef MSLANMAN
+ { "ms-lanman", o_bool, &ms_lanman,
+ "Use LanMan passwd when using MS-CHAP", 1 },
+#endif
+#ifdef DEBUGMPPEKEY
+ { "mschap-challenge", o_string, &mschap_challenge,
+ "specify CHAP challenge" },
+ { "mschap2-peer-challenge", o_string, &mschap2_peer_challenge,
+ "specify CHAP peer challenge" },
+#endif
+ { NULL }
+};
+#endif /* PPP_OPTIONS */
+
+#if PPP_SERVER
+/*
+ * chapms_generate_challenge - generate a challenge for MS-CHAP.
+ * For MS-CHAP the challenge length is fixed at 8 bytes.
+ * The length goes in challenge[0] and the actual challenge starts
+ * at challenge[1].
+ */
+static void chapms_generate_challenge(ppp_pcb *pcb, unsigned char *challenge) {
+ LWIP_UNUSED_ARG(pcb);
+
+ *challenge++ = 8;
+#ifdef DEBUGMPPEKEY
+ if (mschap_challenge && strlen(mschap_challenge) == 8)
+ memcpy(challenge, mschap_challenge, 8);
+ else
+#endif
+ magic_random_bytes(challenge, 8);
+}
+
+static void chapms2_generate_challenge(ppp_pcb *pcb, unsigned char *challenge) {
+ LWIP_UNUSED_ARG(pcb);
+
+ *challenge++ = 16;
+#ifdef DEBUGMPPEKEY
+ if (mschap_challenge && strlen(mschap_challenge) == 16)
+ memcpy(challenge, mschap_challenge, 16);
+ else
+#endif
+ magic_random_bytes(challenge, 16);
+}
+
+static int chapms_verify_response(ppp_pcb *pcb, int id, const char *name,
+ const unsigned char *secret, int secret_len,
+ const unsigned char *challenge, const unsigned char *response,
+ char *message, int message_space) {
+ unsigned char md[MS_CHAP_RESPONSE_LEN];
+ int diff;
+ int challenge_len, response_len;
+ LWIP_UNUSED_ARG(id);
+ LWIP_UNUSED_ARG(name);
+
+ challenge_len = *challenge++; /* skip length, is 8 */
+ response_len = *response++;
+ if (response_len != MS_CHAP_RESPONSE_LEN)
+ goto bad;
+
+#ifndef MSLANMAN
+ if (!response[MS_CHAP_USENT]) {
+ /* Should really propagate this into the error packet. */
+ ppp_notice("Peer request for LANMAN auth not supported");
+ goto bad;
+ }
+#endif
+
+ /* Generate the expected response. */
+ ChapMS(pcb, (const u_char *)challenge, (const char *)secret, secret_len, md);
+
+#ifdef MSLANMAN
+ /* Determine which part of response to verify against */
+ if (!response[MS_CHAP_USENT])
+ diff = memcmp(&response[MS_CHAP_LANMANRESP],
+ &md[MS_CHAP_LANMANRESP], MS_CHAP_LANMANRESP_LEN);
+ else
+#endif
+ diff = memcmp(&response[MS_CHAP_NTRESP], &md[MS_CHAP_NTRESP],
+ MS_CHAP_NTRESP_LEN);
+
+ if (diff == 0) {
+ ppp_slprintf(message, message_space, "Access granted");
+ return 1;
+ }
+
+ bad:
+ /* See comments below for MS-CHAP V2 */
+ ppp_slprintf(message, message_space, "E=691 R=1 C=%0.*B V=0",
+ challenge_len, challenge);
+ return 0;
+}
+
+static int chapms2_verify_response(ppp_pcb *pcb, int id, const char *name,
+ const unsigned char *secret, int secret_len,
+ const unsigned char *challenge, const unsigned char *response,
+ char *message, int message_space) {
+ unsigned char md[MS_CHAP2_RESPONSE_LEN];
+ char saresponse[MS_AUTH_RESPONSE_LENGTH+1];
+ int challenge_len, response_len;
+ LWIP_UNUSED_ARG(id);
+
+ challenge_len = *challenge++; /* skip length, is 16 */
+ response_len = *response++;
+ if (response_len != MS_CHAP2_RESPONSE_LEN)
+ goto bad; /* not even the right length */
+
+ /* Generate the expected response and our mutual auth. */
+ ChapMS2(pcb, (const u_char*)challenge, (const u_char*)&response[MS_CHAP2_PEER_CHALLENGE], name,
+ (const char *)secret, secret_len, md,
+ (unsigned char *)saresponse, MS_CHAP2_AUTHENTICATOR);
+
+ /* compare MDs and send the appropriate status */
+ /*
+ * Per RFC 2759, success message must be formatted as
+ * "S=<auth_string> M=<message>"
+ * where
+ * <auth_string> is the Authenticator Response (mutual auth)
+ * <message> is a text message
+ *
+ * However, some versions of Windows (win98 tested) do not know
+ * about the M=<message> part (required per RFC 2759) and flag
+ * it as an error (reported incorrectly as an encryption error
+ * to the user). Since the RFC requires it, and it can be
+ * useful information, we supply it if the peer is a conforming
+ * system. Luckily (?), win98 sets the Flags field to 0x04
+ * (contrary to RFC requirements) so we can use that to
+ * distinguish between conforming and non-conforming systems.
+ *
+ * Special thanks to Alex Swiridov <say@real.kharkov.ua> for
+ * help debugging this.
+ */
+ if (memcmp(&md[MS_CHAP2_NTRESP], &response[MS_CHAP2_NTRESP],
+ MS_CHAP2_NTRESP_LEN) == 0) {
+ if (response[MS_CHAP2_FLAGS])
+ ppp_slprintf(message, message_space, "S=%s", saresponse);
+ else
+ ppp_slprintf(message, message_space, "S=%s M=%s",
+ saresponse, "Access granted");
+ return 1;
+ }
+
+ bad:
+ /*
+ * Failure message must be formatted as
+ * "E=e R=r C=c V=v M=m"
+ * where
+ * e = error code (we use 691, ERROR_AUTHENTICATION_FAILURE)
+ * r = retry (we use 1, ok to retry)
+ * c = challenge to use for next response, we reuse previous
+ * v = Change Password version supported, we use 0
+ * m = text message
+ *
+ * The M=m part is only for MS-CHAPv2. Neither win2k nor
+ * win98 (others untested) display the message to the user anyway.
+ * They also both ignore the E=e code.
+ *
+ * Note that it's safe to reuse the same challenge as we don't
+ * actually accept another response based on the error message
+ * (and no clients try to resend a response anyway).
+ *
+ * Basically, this whole bit is useless code, even the small
+ * implementation here is only because of overspecification.
+ */
+ ppp_slprintf(message, message_space, "E=691 R=1 C=%0.*B V=0 M=%s",
+ challenge_len, challenge, "Access denied");
+ return 0;
+}
+#endif /* PPP_SERVER */
+
+static void chapms_make_response(ppp_pcb *pcb, unsigned char *response, int id, const char *our_name,
+ const unsigned char *challenge, const char *secret, int secret_len,
+ unsigned char *private_) {
+ LWIP_UNUSED_ARG(id);
+ LWIP_UNUSED_ARG(our_name);
+ LWIP_UNUSED_ARG(private_);
+ challenge++; /* skip length, should be 8 */
+ *response++ = MS_CHAP_RESPONSE_LEN;
+ ChapMS(pcb, challenge, secret, secret_len, response);
+}
+
+static void chapms2_make_response(ppp_pcb *pcb, unsigned char *response, int id, const char *our_name,
+ const unsigned char *challenge, const char *secret, int secret_len,
+ unsigned char *private_) {
+ LWIP_UNUSED_ARG(id);
+ challenge++; /* skip length, should be 16 */
+ *response++ = MS_CHAP2_RESPONSE_LEN;
+ ChapMS2(pcb, challenge,
+#ifdef DEBUGMPPEKEY
+ mschap2_peer_challenge,
+#else
+ NULL,
+#endif
+ our_name, secret, secret_len, response, private_,
+ MS_CHAP2_AUTHENTICATEE);
+}
+
+static int chapms2_check_success(ppp_pcb *pcb, unsigned char *msg, int len, unsigned char *private_) {
+ LWIP_UNUSED_ARG(pcb);
+
+ if ((len < MS_AUTH_RESPONSE_LENGTH + 2) ||
+ strncmp((char *)msg, "S=", 2) != 0) {
+ /* Packet does not start with "S=" */
+ ppp_error("MS-CHAPv2 Success packet is badly formed.");
+ return 0;
+ }
+ msg += 2;
+ len -= 2;
+ if (len < MS_AUTH_RESPONSE_LENGTH
+ || memcmp(msg, private_, MS_AUTH_RESPONSE_LENGTH)) {
+ /* Authenticator Response did not match expected. */
+ ppp_error("MS-CHAPv2 mutual authentication failed.");
+ return 0;
+ }
+ /* Authenticator Response matches. */
+ msg += MS_AUTH_RESPONSE_LENGTH; /* Eat it */
+ len -= MS_AUTH_RESPONSE_LENGTH;
+ if ((len >= 3) && !strncmp((char *)msg, " M=", 3)) {
+ msg += 3; /* Eat the delimiter */
+ } else if (len) {
+ /* Packet has extra text which does not begin " M=" */
+ ppp_error("MS-CHAPv2 Success packet is badly formed.");
+ return 0;
+ }
+ return 1;
+}
+
+static void chapms_handle_failure(ppp_pcb *pcb, unsigned char *inp, int len) {
+ int err;
+ const char *p;
+ char msg[64];
+ LWIP_UNUSED_ARG(pcb);
+
+ /* We want a null-terminated string for strxxx(). */
+ len = LWIP_MIN(len, 63);
+ MEMCPY(msg, inp, len);
+ msg[len] = 0;
+ p = msg;
+
+ /*
+ * Deal with MS-CHAP formatted failure messages; just print the
+ * M=<message> part (if any). For MS-CHAP we're not really supposed
+ * to use M=<message>, but it shouldn't hurt. See
+ * chapms[2]_verify_response.
+ */
+ if (!strncmp(p, "E=", 2))
+ err = strtol(p+2, NULL, 10); /* Remember the error code. */
+ else
+ goto print_msg; /* Message is badly formatted. */
+
+ if (len && ((p = strstr(p, " M=")) != NULL)) {
+ /* M=<message> field found. */
+ p += 3;
+ } else {
+ /* No M=<message>; use the error code. */
+ switch (err) {
+ case MS_CHAP_ERROR_RESTRICTED_LOGON_HOURS:
+ p = "E=646 Restricted logon hours";
+ break;
+
+ case MS_CHAP_ERROR_ACCT_DISABLED:
+ p = "E=647 Account disabled";
+ break;
+
+ case MS_CHAP_ERROR_PASSWD_EXPIRED:
+ p = "E=648 Password expired";
+ break;
+
+ case MS_CHAP_ERROR_NO_DIALIN_PERMISSION:
+ p = "E=649 No dialin permission";
+ break;
+
+ case MS_CHAP_ERROR_AUTHENTICATION_FAILURE:
+ p = "E=691 Authentication failure";
+ break;
+
+ case MS_CHAP_ERROR_CHANGING_PASSWORD:
+ /* Should never see this, we don't support Change Password. */
+ p = "E=709 Error changing password";
+ break;
+
+ default:
+ ppp_error("Unknown MS-CHAP authentication failure: %.*v",
+ len, inp);
+ return;
+ }
+ }
+print_msg:
+ if (p != NULL)
+ ppp_error("MS-CHAP authentication failed: %v", p);
+}
+
+static void ChallengeResponse(const u_char *challenge,
+ const u_char PasswordHash[MD4_SIGNATURE_SIZE],
+ u_char response[24]) {
+ u_char ZPasswordHash[21];
+ lwip_des_context des;
+ u_char des_key[8];
+
+ BZERO(ZPasswordHash, sizeof(ZPasswordHash));
+ MEMCPY(ZPasswordHash, PasswordHash, MD4_SIGNATURE_SIZE);
+
+#if 0
+ dbglog("ChallengeResponse - ZPasswordHash %.*B",
+ sizeof(ZPasswordHash), ZPasswordHash);
+#endif
+
+ pppcrypt_56_to_64_bit_key(ZPasswordHash + 0, des_key);
+ lwip_des_init(&des);
+ lwip_des_setkey_enc(&des, des_key);
+ lwip_des_crypt_ecb(&des, challenge, response +0);
+ lwip_des_free(&des);
+
+ pppcrypt_56_to_64_bit_key(ZPasswordHash + 7, des_key);
+ lwip_des_init(&des);
+ lwip_des_setkey_enc(&des, des_key);
+ lwip_des_crypt_ecb(&des, challenge, response +8);
+ lwip_des_free(&des);
+
+ pppcrypt_56_to_64_bit_key(ZPasswordHash + 14, des_key);
+ lwip_des_init(&des);
+ lwip_des_setkey_enc(&des, des_key);
+ lwip_des_crypt_ecb(&des, challenge, response +16);
+ lwip_des_free(&des);
+
+#if 0
+ dbglog("ChallengeResponse - response %.24B", response);
+#endif
+}
+
+static void ChallengeHash(const u_char PeerChallenge[16], const u_char *rchallenge,
+ const char *username, u_char Challenge[8]) {
+ lwip_sha1_context sha1Context;
+ u_char sha1Hash[SHA1_SIGNATURE_SIZE];
+ const char *user;
+
+ /* remove domain from "domain\username" */
+ if ((user = strrchr(username, '\\')) != NULL)
+ ++user;
+ else
+ user = username;
+
+ lwip_sha1_init(&sha1Context);
+ lwip_sha1_starts(&sha1Context);
+ lwip_sha1_update(&sha1Context, PeerChallenge, 16);
+ lwip_sha1_update(&sha1Context, rchallenge, 16);
+ lwip_sha1_update(&sha1Context, (const unsigned char*)user, strlen(user));
+ lwip_sha1_finish(&sha1Context, sha1Hash);
+ lwip_sha1_free(&sha1Context);
+
+ MEMCPY(Challenge, sha1Hash, 8);
+}
+
+/*
+ * Convert the ASCII version of the password to Unicode.
+ * This implicitly supports 8-bit ISO8859/1 characters.
+ * This gives us the little-endian representation, which
+ * is assumed by all M$ CHAP RFCs. (Unicode byte ordering
+ * is machine-dependent.)
+ */
+static void ascii2unicode(const char ascii[], int ascii_len, u_char unicode[]) {
+ int i;
+
+ BZERO(unicode, ascii_len * 2);
+ for (i = 0; i < ascii_len; i++)
+ unicode[i * 2] = (u_char) ascii[i];
+}
+
+static void NTPasswordHash(u_char *secret, int secret_len, u_char hash[MD4_SIGNATURE_SIZE]) {
+ lwip_md4_context md4Context;
+
+ lwip_md4_init(&md4Context);
+ lwip_md4_starts(&md4Context);
+ lwip_md4_update(&md4Context, secret, secret_len);
+ lwip_md4_finish(&md4Context, hash);
+ lwip_md4_free(&md4Context);
+}
+
+static void ChapMS_NT(const u_char *rchallenge, const char *secret, int secret_len,
+ u_char NTResponse[24]) {
+ u_char unicodePassword[MAX_NT_PASSWORD * 2];
+ u_char PasswordHash[MD4_SIGNATURE_SIZE];
+
+ /* Hash the Unicode version of the secret (== password). */
+ ascii2unicode(secret, secret_len, unicodePassword);
+ NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash);
+
+ ChallengeResponse(rchallenge, PasswordHash, NTResponse);
+}
+
+static void ChapMS2_NT(const u_char *rchallenge, const u_char PeerChallenge[16], const char *username,
+ const char *secret, int secret_len, u_char NTResponse[24]) {
+ u_char unicodePassword[MAX_NT_PASSWORD * 2];
+ u_char PasswordHash[MD4_SIGNATURE_SIZE];
+ u_char Challenge[8];
+
+ ChallengeHash(PeerChallenge, rchallenge, username, Challenge);
+
+ /* Hash the Unicode version of the secret (== password). */
+ ascii2unicode(secret, secret_len, unicodePassword);
+ NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash);
+
+ ChallengeResponse(Challenge, PasswordHash, NTResponse);
+}
+
+#ifdef MSLANMAN
+static u_char *StdText = (u_char *)"KGS!@#$%"; /* key from rasapi32.dll */
+
+static void ChapMS_LANMan(u_char *rchallenge, char *secret, int secret_len,
+ unsigned char *response) {
+ int i;
+ u_char UcasePassword[MAX_NT_PASSWORD]; /* max is actually 14 */
+ u_char PasswordHash[MD4_SIGNATURE_SIZE];
+ lwip_des_context des;
+ u_char des_key[8];
+
+ /* LANMan password is case insensitive */
+ BZERO(UcasePassword, sizeof(UcasePassword));
+ for (i = 0; i < secret_len; i++)
+ UcasePassword[i] = (u_char)toupper(secret[i]);
+
+ pppcrypt_56_to_64_bit_key(UcasePassword +0, des_key);
+ lwip_des_init(&des);
+ lwip_des_setkey_enc(&des, des_key);
+ lwip_des_crypt_ecb(&des, StdText, PasswordHash +0);
+ lwip_des_free(&des);
+
+ pppcrypt_56_to_64_bit_key(UcasePassword +7, des_key);
+ lwip_des_init(&des);
+ lwip_des_setkey_enc(&des, des_key);
+ lwip_des_crypt_ecb(&des, StdText, PasswordHash +8);
+ lwip_des_free(&des);
+
+ ChallengeResponse(rchallenge, PasswordHash, &response[MS_CHAP_LANMANRESP]);
+}
+#endif
+
+
+static void GenerateAuthenticatorResponse(const u_char PasswordHashHash[MD4_SIGNATURE_SIZE],
+ u_char NTResponse[24], const u_char PeerChallenge[16],
+ const u_char *rchallenge, const char *username,
+ u_char authResponse[MS_AUTH_RESPONSE_LENGTH+1]) {
+ /*
+ * "Magic" constants used in response generation, from RFC 2759.
+ */
+ static const u_char Magic1[39] = /* "Magic server to client signing constant" */
+ { 0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76,
+ 0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65,
+ 0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67,
+ 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74 };
+ static const u_char Magic2[41] = /* "Pad to make it do more than one iteration" */
+ { 0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B,
+ 0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F,
+ 0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E,
+ 0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F,
+ 0x6E };
+
+ int i;
+ lwip_sha1_context sha1Context;
+ u_char Digest[SHA1_SIGNATURE_SIZE];
+ u_char Challenge[8];
+
+ lwip_sha1_init(&sha1Context);
+ lwip_sha1_starts(&sha1Context);
+ lwip_sha1_update(&sha1Context, PasswordHashHash, MD4_SIGNATURE_SIZE);
+ lwip_sha1_update(&sha1Context, NTResponse, 24);
+ lwip_sha1_update(&sha1Context, Magic1, sizeof(Magic1));
+ lwip_sha1_finish(&sha1Context, Digest);
+ lwip_sha1_free(&sha1Context);
+
+ ChallengeHash(PeerChallenge, rchallenge, username, Challenge);
+
+ lwip_sha1_init(&sha1Context);
+ lwip_sha1_starts(&sha1Context);
+ lwip_sha1_update(&sha1Context, Digest, sizeof(Digest));
+ lwip_sha1_update(&sha1Context, Challenge, sizeof(Challenge));
+ lwip_sha1_update(&sha1Context, Magic2, sizeof(Magic2));
+ lwip_sha1_finish(&sha1Context, Digest);
+ lwip_sha1_free(&sha1Context);
+
+ /* Convert to ASCII hex string. */
+ for (i = 0; i < LWIP_MAX((MS_AUTH_RESPONSE_LENGTH / 2), (int)sizeof(Digest)); i++)
+ sprintf((char *)&authResponse[i * 2], "%02X", Digest[i]);
+}
+
+
+static void GenerateAuthenticatorResponsePlain(
+ const char *secret, int secret_len,
+ u_char NTResponse[24], const u_char PeerChallenge[16],
+ const u_char *rchallenge, const char *username,
+ u_char authResponse[MS_AUTH_RESPONSE_LENGTH+1]) {
+ u_char unicodePassword[MAX_NT_PASSWORD * 2];
+ u_char PasswordHash[MD4_SIGNATURE_SIZE];
+ u_char PasswordHashHash[MD4_SIGNATURE_SIZE];
+
+ /* Hash (x2) the Unicode version of the secret (== password). */
+ ascii2unicode(secret, secret_len, unicodePassword);
+ NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash);
+ NTPasswordHash(PasswordHash, sizeof(PasswordHash),
+ PasswordHashHash);
+
+ GenerateAuthenticatorResponse(PasswordHashHash, NTResponse, PeerChallenge,
+ rchallenge, username, authResponse);
+}
+
+
+#if MPPE_SUPPORT
+/*
+ * Set mppe_xxxx_key from MS-CHAP credentials. (see RFC 3079)
+ */
+static void Set_Start_Key(ppp_pcb *pcb, const u_char *rchallenge, const char *secret, int secret_len) {
+ u_char unicodePassword[MAX_NT_PASSWORD * 2];
+ u_char PasswordHash[MD4_SIGNATURE_SIZE];
+ u_char PasswordHashHash[MD4_SIGNATURE_SIZE];
+ lwip_sha1_context sha1Context;
+ u_char Digest[SHA1_SIGNATURE_SIZE]; /* >= MPPE_MAX_KEY_LEN */
+
+ /* Hash (x2) the Unicode version of the secret (== password). */
+ ascii2unicode(secret, secret_len, unicodePassword);
+ NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash);
+ NTPasswordHash(PasswordHash, sizeof(PasswordHash), PasswordHashHash);
+
+ lwip_sha1_init(&sha1Context);
+ lwip_sha1_starts(&sha1Context);
+ lwip_sha1_update(&sha1Context, PasswordHashHash, MD4_SIGNATURE_SIZE);
+ lwip_sha1_update(&sha1Context, PasswordHashHash, MD4_SIGNATURE_SIZE);
+ lwip_sha1_update(&sha1Context, rchallenge, 8);
+ lwip_sha1_finish(&sha1Context, Digest);
+ lwip_sha1_free(&sha1Context);
+
+ /* Same key in both directions. */
+ mppe_set_key(pcb, &pcb->mppe_comp, Digest);
+ mppe_set_key(pcb, &pcb->mppe_decomp, Digest);
+
+ pcb->mppe_keys_set = 1;
+}
+
+/*
+ * Set mppe_xxxx_key from MS-CHAPv2 credentials. (see RFC 3079)
+ */
+static void SetMasterKeys(ppp_pcb *pcb, const char *secret, int secret_len, u_char NTResponse[24], int IsServer) {
+ u_char unicodePassword[MAX_NT_PASSWORD * 2];
+ u_char PasswordHash[MD4_SIGNATURE_SIZE];
+ u_char PasswordHashHash[MD4_SIGNATURE_SIZE];
+ lwip_sha1_context sha1Context;
+ u_char MasterKey[SHA1_SIGNATURE_SIZE]; /* >= MPPE_MAX_KEY_LEN */
+ u_char Digest[SHA1_SIGNATURE_SIZE]; /* >= MPPE_MAX_KEY_LEN */
+ const u_char *s;
+
+ /* "This is the MPPE Master Key" */
+ static const u_char Magic1[27] =
+ { 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x4d, 0x50, 0x50, 0x45, 0x20, 0x4d,
+ 0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4b, 0x65, 0x79 };
+ /* "On the client side, this is the send key; "
+ "on the server side, it is the receive key." */
+ static const u_char Magic2[84] =
+ { 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
+ 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
+ 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
+ 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x6b, 0x65, 0x79,
+ 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73,
+ 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, 0x69, 0x64, 0x65,
+ 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
+ 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
+ 0x6b, 0x65, 0x79, 0x2e };
+ /* "On the client side, this is the receive key; "
+ "on the server side, it is the send key." */
+ static const u_char Magic3[84] =
+ { 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
+ 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
+ 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
+ 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
+ 0x6b, 0x65, 0x79, 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68,
+ 0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73,
+ 0x69, 0x64, 0x65, 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73,
+ 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20,
+ 0x6b, 0x65, 0x79, 0x2e };
+
+ /* Hash (x2) the Unicode version of the secret (== password). */
+ ascii2unicode(secret, secret_len, unicodePassword);
+ NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash);
+ NTPasswordHash(PasswordHash, sizeof(PasswordHash), PasswordHashHash);
+
+ lwip_sha1_init(&sha1Context);
+ lwip_sha1_starts(&sha1Context);
+ lwip_sha1_update(&sha1Context, PasswordHashHash, MD4_SIGNATURE_SIZE);
+ lwip_sha1_update(&sha1Context, NTResponse, 24);
+ lwip_sha1_update(&sha1Context, Magic1, sizeof(Magic1));
+ lwip_sha1_finish(&sha1Context, MasterKey);
+ lwip_sha1_free(&sha1Context);
+
+ /*
+ * generate send key
+ */
+ if (IsServer)
+ s = Magic3;
+ else
+ s = Magic2;
+ lwip_sha1_init(&sha1Context);
+ lwip_sha1_starts(&sha1Context);
+ lwip_sha1_update(&sha1Context, MasterKey, 16);
+ lwip_sha1_update(&sha1Context, mppe_sha1_pad1, SHA1_PAD_SIZE);
+ lwip_sha1_update(&sha1Context, s, 84);
+ lwip_sha1_update(&sha1Context, mppe_sha1_pad2, SHA1_PAD_SIZE);
+ lwip_sha1_finish(&sha1Context, Digest);
+ lwip_sha1_free(&sha1Context);
+
+ mppe_set_key(pcb, &pcb->mppe_comp, Digest);
+
+ /*
+ * generate recv key
+ */
+ if (IsServer)
+ s = Magic2;
+ else
+ s = Magic3;
+ lwip_sha1_init(&sha1Context);
+ lwip_sha1_starts(&sha1Context);
+ lwip_sha1_update(&sha1Context, MasterKey, 16);
+ lwip_sha1_update(&sha1Context, mppe_sha1_pad1, SHA1_PAD_SIZE);
+ lwip_sha1_update(&sha1Context, s, 84);
+ lwip_sha1_update(&sha1Context, mppe_sha1_pad2, SHA1_PAD_SIZE);
+ lwip_sha1_finish(&sha1Context, Digest);
+ lwip_sha1_free(&sha1Context);
+
+ mppe_set_key(pcb, &pcb->mppe_decomp, Digest);
+
+ pcb->mppe_keys_set = 1;
+}
+
+#endif /* MPPE_SUPPORT */
+
+
+static void ChapMS(ppp_pcb *pcb, const u_char *rchallenge, const char *secret, int secret_len,
+ unsigned char *response) {
+#if !MPPE_SUPPORT
+ LWIP_UNUSED_ARG(pcb);
+#endif /* !MPPE_SUPPORT */
+ BZERO(response, MS_CHAP_RESPONSE_LEN);
+
+ ChapMS_NT(rchallenge, secret, secret_len, &response[MS_CHAP_NTRESP]);
+
+#ifdef MSLANMAN
+ ChapMS_LANMan(rchallenge, secret, secret_len,
+ &response[MS_CHAP_LANMANRESP]);
+
+ /* preferred method is set by option */
+ response[MS_CHAP_USENT] = !ms_lanman;
+#else
+ response[MS_CHAP_USENT] = 1;
+#endif
+
+#if MPPE_SUPPORT
+ Set_Start_Key(pcb, rchallenge, secret, secret_len);
+#endif /* MPPE_SUPPORT */
+}
+
+
+/*
+ * If PeerChallenge is NULL, one is generated and the PeerChallenge
+ * field of response is filled in. Call this way when generating a response.
+ * If PeerChallenge is supplied, it is copied into the PeerChallenge field.
+ * Call this way when verifying a response (or debugging).
+ * Do not call with PeerChallenge = response.
+ *
+ * The PeerChallenge field of response is then used for calculation of the
+ * Authenticator Response.
+ */
+static void ChapMS2(ppp_pcb *pcb, const u_char *rchallenge, const u_char *PeerChallenge,
+ const char *user, const char *secret, int secret_len, unsigned char *response,
+ u_char authResponse[], int authenticator) {
+ /* ARGSUSED */
+ LWIP_UNUSED_ARG(authenticator);
+#if !MPPE_SUPPORT
+ LWIP_UNUSED_ARG(pcb);
+#endif /* !MPPE_SUPPORT */
+
+ BZERO(response, MS_CHAP2_RESPONSE_LEN);
+
+ /* Generate the Peer-Challenge if requested, or copy it if supplied. */
+ if (!PeerChallenge)
+ magic_random_bytes(&response[MS_CHAP2_PEER_CHALLENGE], MS_CHAP2_PEER_CHAL_LEN);
+ else
+ MEMCPY(&response[MS_CHAP2_PEER_CHALLENGE], PeerChallenge,
+ MS_CHAP2_PEER_CHAL_LEN);
+
+ /* Generate the NT-Response */
+ ChapMS2_NT(rchallenge, &response[MS_CHAP2_PEER_CHALLENGE], user,
+ secret, secret_len, &response[MS_CHAP2_NTRESP]);
+
+ /* Generate the Authenticator Response. */
+ GenerateAuthenticatorResponsePlain(secret, secret_len,
+ &response[MS_CHAP2_NTRESP],
+ &response[MS_CHAP2_PEER_CHALLENGE],
+ rchallenge, user, authResponse);
+
+#if MPPE_SUPPORT
+ SetMasterKeys(pcb, secret, secret_len,
+ &response[MS_CHAP2_NTRESP], authenticator);
+#endif /* MPPE_SUPPORT */
+}
+
+#if 0 /* UNUSED */
+#if MPPE_SUPPORT
+/*
+ * Set MPPE options from plugins.
+ */
+void set_mppe_enc_types(int policy, int types) {
+ /* Early exit for unknown policies. */
+ if (policy != MPPE_ENC_POL_ENC_ALLOWED ||
+ policy != MPPE_ENC_POL_ENC_REQUIRED)
+ return;
+
+ /* Don't modify MPPE if it's optional and wasn't already configured. */
+ if (policy == MPPE_ENC_POL_ENC_ALLOWED && !ccp_wantoptions[0].mppe)
+ return;
+
+ /*
+ * Disable undesirable encryption types. Note that we don't ENABLE
+ * any encryption types, to avoid overriding manual configuration.
+ */
+ switch(types) {
+ case MPPE_ENC_TYPES_RC4_40:
+ ccp_wantoptions[0].mppe &= ~MPPE_OPT_128; /* disable 128-bit */
+ break;
+ case MPPE_ENC_TYPES_RC4_128:
+ ccp_wantoptions[0].mppe &= ~MPPE_OPT_40; /* disable 40-bit */
+ break;
+ default:
+ break;
+ }
+}
+#endif /* MPPE_SUPPORT */
+#endif /* UNUSED */
+
+const struct chap_digest_type chapms_digest = {
+ CHAP_MICROSOFT, /* code */
+#if PPP_SERVER
+ chapms_generate_challenge,
+ chapms_verify_response,
+#endif /* PPP_SERVER */
+ chapms_make_response,
+ NULL, /* check_success */
+ chapms_handle_failure,
+};
+
+const struct chap_digest_type chapms2_digest = {
+ CHAP_MICROSOFT_V2, /* code */
+#if PPP_SERVER
+ chapms2_generate_challenge,
+ chapms2_verify_response,
+#endif /* PPP_SERVER */
+ chapms2_make_response,
+ chapms2_check_success,
+ chapms_handle_failure,
+};
+
+#endif /* PPP_SUPPORT && MSCHAP_SUPPORT */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/demand.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/demand.c
new file mode 100644
index 0000000..26c6c30
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/demand.c
@@ -0,0 +1,465 @@
+/*
+ * demand.c - Support routines for demand-dialling.
+ *
+ * Copyright (c) 1996-2002 Paul Mackerras. 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. The name(s) of the authors of this software must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission.
+ *
+ * 3. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by Paul Mackerras
+ * <paulus@samba.org>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT && DEMAND_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#ifdef PPP_FILTER
+#include <pcap-bpf.h>
+#endif
+
+#include "netif/ppp/ppp_impl.h"
+
+#include "netif/ppp/fsm.h"
+#include "netif/ppp/ipcp.h"
+#include "netif/ppp/lcp.h"
+
+char *frame;
+int framelen;
+int framemax;
+int escape_flag;
+int flush_flag;
+int fcs;
+
+struct packet {
+ int length;
+ struct packet *next;
+ unsigned char data[1];
+};
+
+struct packet *pend_q;
+struct packet *pend_qtail;
+
+static int active_packet (unsigned char *, int);
+
+/*
+ * demand_conf - configure the interface for doing dial-on-demand.
+ */
+void
+demand_conf()
+{
+ int i;
+ const struct protent *protp;
+
+/* framemax = lcp_allowoptions[0].mru;
+ if (framemax < PPP_MRU) */
+ framemax = PPP_MRU;
+ framemax += PPP_HDRLEN + PPP_FCSLEN;
+ frame = malloc(framemax);
+ if (frame == NULL)
+ novm("demand frame");
+ framelen = 0;
+ pend_q = NULL;
+ escape_flag = 0;
+ flush_flag = 0;
+ fcs = PPP_INITFCS;
+
+ netif_set_mtu(pcb, LWIP_MIN(lcp_allowoptions[0].mru, PPP_MRU));
+ if (ppp_send_config(pcb, PPP_MRU, (u32_t) 0, 0, 0) < 0
+ || ppp_recv_config(pcb, PPP_MRU, (u32_t) 0, 0, 0) < 0)
+ fatal("Couldn't set up demand-dialled PPP interface: %m");
+
+#ifdef PPP_FILTER
+ set_filters(&pass_filter, &active_filter);
+#endif
+
+ /*
+ * Call the demand_conf procedure for each protocol that's got one.
+ */
+ for (i = 0; (protp = protocols[i]) != NULL; ++i)
+ if (protp->demand_conf != NULL)
+ ((*protp->demand_conf)(pcb));
+/* FIXME: find a way to die() here */
+#if 0
+ if (!((*protp->demand_conf)(pcb)))
+ die(1);
+#endif
+}
+
+
+/*
+ * demand_block - set each network protocol to block further packets.
+ */
+void
+demand_block()
+{
+ int i;
+ const struct protent *protp;
+
+ for (i = 0; (protp = protocols[i]) != NULL; ++i)
+ if (protp->demand_conf != NULL)
+ sifnpmode(pcb, protp->protocol & ~0x8000, NPMODE_QUEUE);
+ get_loop_output();
+}
+
+/*
+ * demand_discard - set each network protocol to discard packets
+ * with an error.
+ */
+void
+demand_discard()
+{
+ struct packet *pkt, *nextpkt;
+ int i;
+ const struct protent *protp;
+
+ for (i = 0; (protp = protocols[i]) != NULL; ++i)
+ if (protp->demand_conf != NULL)
+ sifnpmode(pcb, protp->protocol & ~0x8000, NPMODE_ERROR);
+ get_loop_output();
+
+ /* discard all saved packets */
+ for (pkt = pend_q; pkt != NULL; pkt = nextpkt) {
+ nextpkt = pkt->next;
+ free(pkt);
+ }
+ pend_q = NULL;
+ framelen = 0;
+ flush_flag = 0;
+ escape_flag = 0;
+ fcs = PPP_INITFCS;
+}
+
+/*
+ * demand_unblock - set each enabled network protocol to pass packets.
+ */
+void
+demand_unblock()
+{
+ int i;
+ const struct protent *protp;
+
+ for (i = 0; (protp = protocols[i]) != NULL; ++i)
+ if (protp->demand_conf != NULL)
+ sifnpmode(pcb, protp->protocol & ~0x8000, NPMODE_PASS);
+}
+
+/*
+ * FCS lookup table as calculated by genfcstab.
+ */
+static 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
+};
+
+/*
+ * loop_chars - process characters received from the loopback.
+ * Calls loop_frame when a complete frame has been accumulated.
+ * Return value is 1 if we need to bring up the link, 0 otherwise.
+ */
+int
+loop_chars(p, n)
+ unsigned char *p;
+ int n;
+{
+ int c, rv;
+
+ rv = 0;
+
+/* check for synchronous connection... */
+
+ if ( (p[0] == 0xFF) && (p[1] == 0x03) ) {
+ rv = loop_frame(p,n);
+ return rv;
+ }
+
+ for (; n > 0; --n) {
+ c = *p++;
+ if (c == PPP_FLAG) {
+ if (!escape_flag && !flush_flag
+ && framelen > 2 && fcs == PPP_GOODFCS) {
+ framelen -= 2;
+ if (loop_frame((unsigned char *)frame, framelen))
+ rv = 1;
+ }
+ framelen = 0;
+ flush_flag = 0;
+ escape_flag = 0;
+ fcs = PPP_INITFCS;
+ continue;
+ }
+ if (flush_flag)
+ continue;
+ if (escape_flag) {
+ c ^= PPP_TRANS;
+ escape_flag = 0;
+ } else if (c == PPP_ESCAPE) {
+ escape_flag = 1;
+ continue;
+ }
+ if (framelen >= framemax) {
+ flush_flag = 1;
+ continue;
+ }
+ frame[framelen++] = c;
+ fcs = PPP_FCS(fcs, c);
+ }
+ return rv;
+}
+
+/*
+ * loop_frame - given a frame obtained from the loopback,
+ * decide whether to bring up the link or not, and, if we want
+ * to transmit this frame later, put it on the pending queue.
+ * Return value is 1 if we need to bring up the link, 0 otherwise.
+ * We assume that the kernel driver has already applied the
+ * pass_filter, so we won't get packets it rejected.
+ * We apply the active_filter to see if we want this packet to
+ * bring up the link.
+ */
+int
+loop_frame(frame, len)
+ unsigned char *frame;
+ int len;
+{
+ struct packet *pkt;
+
+ /* dbglog("from loop: %P", frame, len); */
+ if (len < PPP_HDRLEN)
+ return 0;
+ if ((PPP_PROTOCOL(frame) & 0x8000) != 0)
+ return 0; /* shouldn't get any of these anyway */
+ if (!active_packet(frame, len))
+ return 0;
+
+ pkt = (struct packet *) malloc(sizeof(struct packet) + len);
+ if (pkt != NULL) {
+ pkt->length = len;
+ pkt->next = NULL;
+ memcpy(pkt->data, frame, len);
+ if (pend_q == NULL)
+ pend_q = pkt;
+ else
+ pend_qtail->next = pkt;
+ pend_qtail = pkt;
+ }
+ return 1;
+}
+
+/*
+ * demand_rexmit - Resend all those frames which we got via the
+ * loopback, now that the real serial link is up.
+ */
+void
+demand_rexmit(proto, newip)
+ int proto;
+ u32_t newip;
+{
+ struct packet *pkt, *prev, *nextpkt;
+ unsigned short checksum;
+ unsigned short pkt_checksum = 0;
+ unsigned iphdr;
+ struct timeval tv;
+ char cv = 0;
+ char ipstr[16];
+
+ prev = NULL;
+ pkt = pend_q;
+ pend_q = NULL;
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+ select(0,NULL,NULL,NULL,&tv); /* Sleep for 1 Seconds */
+ for (; pkt != NULL; pkt = nextpkt) {
+ nextpkt = pkt->next;
+ if (PPP_PROTOCOL(pkt->data) == proto) {
+ if ( (proto == PPP_IP) && newip ) {
+ /* Get old checksum */
+
+ iphdr = (pkt->data[4] & 15) << 2;
+ checksum = *((unsigned short *) (pkt->data+14));
+ if (checksum == 0xFFFF) {
+ checksum = 0;
+ }
+
+
+ if (pkt->data[13] == 17) {
+ pkt_checksum = *((unsigned short *) (pkt->data+10+iphdr));
+ if (pkt_checksum) {
+ cv = 1;
+ if (pkt_checksum == 0xFFFF) {
+ pkt_checksum = 0;
+ }
+ }
+ else {
+ cv = 0;
+ }
+ }
+
+ if (pkt->data[13] == 6) {
+ pkt_checksum = *((unsigned short *) (pkt->data+20+iphdr));
+ cv = 1;
+ if (pkt_checksum == 0xFFFF) {
+ pkt_checksum = 0;
+ }
+ }
+
+ /* Delete old Source-IP-Address */
+ checksum -= *((unsigned short *) (pkt->data+16)) ^ 0xFFFF;
+ checksum -= *((unsigned short *) (pkt->data+18)) ^ 0xFFFF;
+
+ pkt_checksum -= *((unsigned short *) (pkt->data+16)) ^ 0xFFFF;
+ pkt_checksum -= *((unsigned short *) (pkt->data+18)) ^ 0xFFFF;
+
+ /* Change Source-IP-Address */
+ * ((u32_t *) (pkt->data + 16)) = newip;
+
+ /* Add new Source-IP-Address */
+ checksum += *((unsigned short *) (pkt->data+16)) ^ 0xFFFF;
+ checksum += *((unsigned short *) (pkt->data+18)) ^ 0xFFFF;
+
+ pkt_checksum += *((unsigned short *) (pkt->data+16)) ^ 0xFFFF;
+ pkt_checksum += *((unsigned short *) (pkt->data+18)) ^ 0xFFFF;
+
+ /* Write new checksum */
+ if (!checksum) {
+ checksum = 0xFFFF;
+ }
+ *((unsigned short *) (pkt->data+14)) = checksum;
+ if (pkt->data[13] == 6) {
+ *((unsigned short *) (pkt->data+20+iphdr)) = pkt_checksum;
+ }
+ if (cv && (pkt->data[13] == 17) ) {
+ *((unsigned short *) (pkt->data+10+iphdr)) = pkt_checksum;
+ }
+
+ /* Log Packet */
+ strcpy(ipstr,inet_ntoa(*( (struct in_addr *) (pkt->data+16))));
+ if (pkt->data[13] == 1) {
+ syslog(LOG_INFO,"Open ICMP %s -> %s\n",
+ ipstr,
+ inet_ntoa(*( (struct in_addr *) (pkt->data+20))));
+ } else {
+ syslog(LOG_INFO,"Open %s %s:%d -> %s:%d\n",
+ pkt->data[13] == 6 ? "TCP" : "UDP",
+ ipstr,
+ ntohs(*( (short *) (pkt->data+iphdr+4))),
+ inet_ntoa(*( (struct in_addr *) (pkt->data+20))),
+ ntohs(*( (short *) (pkt->data+iphdr+6))));
+ }
+ }
+ output(pcb, pkt->data, pkt->length);
+ free(pkt);
+ } else {
+ if (prev == NULL)
+ pend_q = pkt;
+ else
+ prev->next = pkt;
+ prev = pkt;
+ }
+ }
+ pend_qtail = prev;
+ if (prev != NULL)
+ prev->next = NULL;
+}
+
+/*
+ * Scan a packet to decide whether it is an "active" packet,
+ * that is, whether it is worth bringing up the link for.
+ */
+static int
+active_packet(p, len)
+ unsigned char *p;
+ int len;
+{
+ int proto, i;
+ const struct protent *protp;
+
+ if (len < PPP_HDRLEN)
+ return 0;
+ proto = PPP_PROTOCOL(p);
+#ifdef PPP_FILTER
+ p[0] = 1; /* outbound packet indicator */
+ if ((pass_filter.bf_len != 0
+ && bpf_filter(pass_filter.bf_insns, p, len, len) == 0)
+ || (active_filter.bf_len != 0
+ && bpf_filter(active_filter.bf_insns, p, len, len) == 0)) {
+ p[0] = 0xff;
+ return 0;
+ }
+ p[0] = 0xff;
+#endif
+ for (i = 0; (protp = protocols[i]) != NULL; ++i) {
+ if (protp->protocol < 0xC000 && (protp->protocol & ~0x8000) == proto) {
+ if (protp->active_pkt == NULL)
+ return 1;
+ return (*protp->active_pkt)(p, len);
+ }
+ }
+ return 0; /* not a supported protocol !!?? */
+}
+
+#endif /* PPP_SUPPORT && DEMAND_SUPPORT */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/eap.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/eap.c
new file mode 100644
index 0000000..8fb5636
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/eap.c
@@ -0,0 +1,2423 @@
+/*
+ * eap.c - Extensible Authentication Protocol for PPP (RFC 2284)
+ *
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ *
+ * Non-exclusive rights to redistribute, modify, translate, and use
+ * this software in source and binary forms, in whole or in part, is
+ * hereby granted, provided that the above copyright notice is
+ * duplicated in any source form, and that neither the name of the
+ * copyright holder nor the author is used to endorse or promote
+ * products derived from this software.
+ *
+ * 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.
+ *
+ * Original version by James Carlson
+ *
+ * This implementation of EAP supports MD5-Challenge and SRP-SHA1
+ * authentication styles. Note that support of MD5-Challenge is a
+ * requirement of RFC 2284, and that it's essentially just a
+ * reimplementation of regular RFC 1994 CHAP using EAP messages.
+ *
+ * As an authenticator ("server"), there are multiple phases for each
+ * style. In the first phase of each style, the unauthenticated peer
+ * name is queried using the EAP Identity request type. If the
+ * "remotename" option is used, then this phase is skipped, because
+ * the peer's name is presumed to be known.
+ *
+ * For MD5-Challenge, there are two phases, and the second phase
+ * consists of sending the challenge itself and handling the
+ * associated response.
+ *
+ * For SRP-SHA1, there are four phases. The second sends 's', 'N',
+ * and 'g'. The reply contains 'A'. The third sends 'B', and the
+ * reply contains 'M1'. The forth sends the 'M2' value.
+ *
+ * As an authenticatee ("client"), there's just a single phase --
+ * responding to the queries generated by the peer. EAP is an
+ * authenticator-driven protocol.
+ *
+ * Based on draft-ietf-pppext-eap-srp-03.txt.
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT && EAP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#include "netif/ppp/ppp_impl.h"
+#include "netif/ppp/eap.h"
+#include "netif/ppp/magic.h"
+#include "netif/ppp/pppcrypt.h"
+
+#ifdef USE_SRP
+#include <t_pwd.h>
+#include <t_server.h>
+#include <t_client.h>
+#endif /* USE_SRP */
+
+#ifndef SHA_DIGESTSIZE
+#define SHA_DIGESTSIZE 20
+#endif
+
+#ifdef USE_SRP
+static char *pn_secret = NULL; /* Pseudonym generating secret */
+#endif
+
+#if PPP_OPTIONS
+/*
+ * Command-line options.
+ */
+static option_t eap_option_list[] = {
+ { "eap-restart", o_int, &eap_states[0].es_server.ea_timeout,
+ "Set retransmit timeout for EAP Requests (server)" },
+ { "eap-max-sreq", o_int, &eap_states[0].es_server.ea_maxrequests,
+ "Set max number of EAP Requests sent (server)" },
+ { "eap-timeout", o_int, &eap_states[0].es_client.ea_timeout,
+ "Set time limit for peer EAP authentication" },
+ { "eap-max-rreq", o_int, &eap_states[0].es_client.ea_maxrequests,
+ "Set max number of EAP Requests allows (client)" },
+ { "eap-interval", o_int, &eap_states[0].es_rechallenge,
+ "Set interval for EAP rechallenge" },
+#ifdef USE_SRP
+ { "srp-interval", o_int, &eap_states[0].es_lwrechallenge,
+ "Set interval for SRP lightweight rechallenge" },
+ { "srp-pn-secret", o_string, &pn_secret,
+ "Long term pseudonym generation secret" },
+ { "srp-use-pseudonym", o_bool, &eap_states[0].es_usepseudo,
+ "Use pseudonym if offered one by server", 1 },
+#endif
+ { NULL }
+};
+#endif /* PPP_OPTIONS */
+
+/*
+ * Protocol entry points.
+ */
+static void eap_init(ppp_pcb *pcb);
+static void eap_input(ppp_pcb *pcb, u_char *inp, int inlen);
+static void eap_protrej(ppp_pcb *pcb);
+static void eap_lowerup(ppp_pcb *pcb);
+static void eap_lowerdown(ppp_pcb *pcb);
+#if PRINTPKT_SUPPORT
+static int eap_printpkt(const u_char *inp, int inlen,
+ void (*)(void *arg, const char *fmt, ...), void *arg);
+#endif /* PRINTPKT_SUPPORT */
+
+const struct protent eap_protent = {
+ PPP_EAP, /* protocol number */
+ eap_init, /* initialization procedure */
+ eap_input, /* process a received packet */
+ eap_protrej, /* process a received protocol-reject */
+ eap_lowerup, /* lower layer has gone up */
+ eap_lowerdown, /* lower layer has gone down */
+ NULL, /* open the protocol */
+ NULL, /* close the protocol */
+#if PRINTPKT_SUPPORT
+ eap_printpkt, /* print a packet in readable form */
+#endif /* PRINTPKT_SUPPORT */
+#if PPP_DATAINPUT
+ NULL, /* process a received data packet */
+#endif /* PPP_DATAINPUT */
+#if PRINTPKT_SUPPORT
+ "EAP", /* text name of protocol */
+ NULL, /* text name of corresponding data protocol */
+#endif /* PRINTPKT_SUPPORT */
+#if PPP_OPTIONS
+ eap_option_list, /* list of command-line options */
+ NULL, /* check requested options; assign defaults */
+#endif /* PPP_OPTIONS */
+#if DEMAND_SUPPORT
+ NULL, /* configure interface for demand-dial */
+ NULL /* say whether to bring up link for this pkt */
+#endif /* DEMAND_SUPPORT */
+};
+
+#ifdef USE_SRP
+/*
+ * A well-known 2048 bit modulus.
+ */
+static const u_char wkmodulus[] = {
+ 0xAC, 0x6B, 0xDB, 0x41, 0x32, 0x4A, 0x9A, 0x9B,
+ 0xF1, 0x66, 0xDE, 0x5E, 0x13, 0x89, 0x58, 0x2F,
+ 0xAF, 0x72, 0xB6, 0x65, 0x19, 0x87, 0xEE, 0x07,
+ 0xFC, 0x31, 0x92, 0x94, 0x3D, 0xB5, 0x60, 0x50,
+ 0xA3, 0x73, 0x29, 0xCB, 0xB4, 0xA0, 0x99, 0xED,
+ 0x81, 0x93, 0xE0, 0x75, 0x77, 0x67, 0xA1, 0x3D,
+ 0xD5, 0x23, 0x12, 0xAB, 0x4B, 0x03, 0x31, 0x0D,
+ 0xCD, 0x7F, 0x48, 0xA9, 0xDA, 0x04, 0xFD, 0x50,
+ 0xE8, 0x08, 0x39, 0x69, 0xED, 0xB7, 0x67, 0xB0,
+ 0xCF, 0x60, 0x95, 0x17, 0x9A, 0x16, 0x3A, 0xB3,
+ 0x66, 0x1A, 0x05, 0xFB, 0xD5, 0xFA, 0xAA, 0xE8,
+ 0x29, 0x18, 0xA9, 0x96, 0x2F, 0x0B, 0x93, 0xB8,
+ 0x55, 0xF9, 0x79, 0x93, 0xEC, 0x97, 0x5E, 0xEA,
+ 0xA8, 0x0D, 0x74, 0x0A, 0xDB, 0xF4, 0xFF, 0x74,
+ 0x73, 0x59, 0xD0, 0x41, 0xD5, 0xC3, 0x3E, 0xA7,
+ 0x1D, 0x28, 0x1E, 0x44, 0x6B, 0x14, 0x77, 0x3B,
+ 0xCA, 0x97, 0xB4, 0x3A, 0x23, 0xFB, 0x80, 0x16,
+ 0x76, 0xBD, 0x20, 0x7A, 0x43, 0x6C, 0x64, 0x81,
+ 0xF1, 0xD2, 0xB9, 0x07, 0x87, 0x17, 0x46, 0x1A,
+ 0x5B, 0x9D, 0x32, 0xE6, 0x88, 0xF8, 0x77, 0x48,
+ 0x54, 0x45, 0x23, 0xB5, 0x24, 0xB0, 0xD5, 0x7D,
+ 0x5E, 0xA7, 0x7A, 0x27, 0x75, 0xD2, 0xEC, 0xFA,
+ 0x03, 0x2C, 0xFB, 0xDB, 0xF5, 0x2F, 0xB3, 0x78,
+ 0x61, 0x60, 0x27, 0x90, 0x04, 0xE5, 0x7A, 0xE6,
+ 0xAF, 0x87, 0x4E, 0x73, 0x03, 0xCE, 0x53, 0x29,
+ 0x9C, 0xCC, 0x04, 0x1C, 0x7B, 0xC3, 0x08, 0xD8,
+ 0x2A, 0x56, 0x98, 0xF3, 0xA8, 0xD0, 0xC3, 0x82,
+ 0x71, 0xAE, 0x35, 0xF8, 0xE9, 0xDB, 0xFB, 0xB6,
+ 0x94, 0xB5, 0xC8, 0x03, 0xD8, 0x9F, 0x7A, 0xE4,
+ 0x35, 0xDE, 0x23, 0x6D, 0x52, 0x5F, 0x54, 0x75,
+ 0x9B, 0x65, 0xE3, 0x72, 0xFC, 0xD6, 0x8E, 0xF2,
+ 0x0F, 0xA7, 0x11, 0x1F, 0x9E, 0x4A, 0xFF, 0x73
+};
+#endif
+
+#if PPP_SERVER
+/* Local forward declarations. */
+static void eap_server_timeout(void *arg);
+#endif /* PPP_SERVER */
+
+/*
+ * Convert EAP state code to printable string for debug.
+ */
+static const char * eap_state_name(enum eap_state_code esc)
+{
+ static const char *state_names[] = { EAP_STATES };
+
+ return (state_names[(int)esc]);
+}
+
+/*
+ * eap_init - Initialize state for an EAP user. This is currently
+ * called once by main() during start-up.
+ */
+static void eap_init(ppp_pcb *pcb) {
+
+ BZERO(&pcb->eap, sizeof(eap_state));
+#if PPP_SERVER
+ pcb->eap.es_server.ea_id = magic();
+#endif /* PPP_SERVER */
+}
+
+/*
+ * eap_client_timeout - Give up waiting for the peer to send any
+ * Request messages.
+ */
+static void eap_client_timeout(void *arg) {
+ ppp_pcb *pcb = (ppp_pcb*)arg;
+
+ if (!eap_client_active(pcb))
+ return;
+
+ ppp_error("EAP: timeout waiting for Request from peer");
+ auth_withpeer_fail(pcb, PPP_EAP);
+ pcb->eap.es_client.ea_state = eapBadAuth;
+}
+
+/*
+ * eap_authwithpeer - Authenticate to our peer (behave as client).
+ *
+ * Start client state and wait for requests. This is called only
+ * after eap_lowerup.
+ */
+void eap_authwithpeer(ppp_pcb *pcb, const char *localname) {
+
+ if(NULL == localname)
+ return;
+
+ /* Save the peer name we're given */
+ pcb->eap.es_client.ea_name = localname;
+ pcb->eap.es_client.ea_namelen = strlen(localname);
+
+ pcb->eap.es_client.ea_state = eapListen;
+
+ /*
+ * Start a timer so that if the other end just goes
+ * silent, we don't sit here waiting forever.
+ */
+ if (pcb->settings.eap_req_time > 0)
+ TIMEOUT(eap_client_timeout, pcb,
+ pcb->settings.eap_req_time);
+}
+
+#if PPP_SERVER
+/*
+ * Format a standard EAP Failure message and send it to the peer.
+ * (Server operation)
+ */
+static void eap_send_failure(ppp_pcb *pcb) {
+ struct pbuf *p;
+ u_char *outp;
+
+ p = pbuf_alloc(PBUF_RAW, (u16_t)(PPP_HDRLEN + EAP_HEADERLEN), PPP_CTRL_PBUF_TYPE);
+ if(NULL == p)
+ return;
+ if(p->tot_len != p->len) {
+ pbuf_free(p);
+ return;
+ }
+
+ outp = (u_char*)p->payload;
+
+ MAKEHEADER(outp, PPP_EAP);
+
+ PUTCHAR(EAP_FAILURE, outp);
+ pcb->eap.es_server.ea_id++;
+ PUTCHAR(pcb->eap.es_server.ea_id, outp);
+ PUTSHORT(EAP_HEADERLEN, outp);
+
+ ppp_write(pcb, p);
+
+ pcb->eap.es_server.ea_state = eapBadAuth;
+ auth_peer_fail(pcb, PPP_EAP);
+}
+
+/*
+ * Format a standard EAP Success message and send it to the peer.
+ * (Server operation)
+ */
+static void eap_send_success(ppp_pcb *pcb) {
+ struct pbuf *p;
+ u_char *outp;
+
+ p = pbuf_alloc(PBUF_RAW, (u16_t)(PPP_HDRLEN + EAP_HEADERLEN), PPP_CTRL_PBUF_TYPE);
+ if(NULL == p)
+ return;
+ if(p->tot_len != p->len) {
+ pbuf_free(p);
+ return;
+ }
+
+ outp = (u_char*)p->payload;
+
+ MAKEHEADER(outp, PPP_EAP);
+
+ PUTCHAR(EAP_SUCCESS, outp);
+ pcb->eap.es_server.ea_id++;
+ PUTCHAR(pcb->eap.es_server.ea_id, outp);
+ PUTSHORT(EAP_HEADERLEN, outp);
+
+ ppp_write(pcb, p);
+
+ auth_peer_success(pcb, PPP_EAP, 0,
+ pcb->eap.es_server.ea_peer, pcb->eap.es_server.ea_peerlen);
+}
+#endif /* PPP_SERVER */
+
+#ifdef USE_SRP
+/*
+ * Set DES key according to pseudonym-generating secret and current
+ * date.
+ */
+static bool
+pncrypt_setkey(int timeoffs)
+{
+ struct tm *tp;
+ char tbuf[9];
+ SHA1_CTX ctxt;
+ u_char dig[SHA_DIGESTSIZE];
+ time_t reftime;
+
+ if (pn_secret == NULL)
+ return (0);
+ reftime = time(NULL) + timeoffs;
+ tp = localtime(&reftime);
+ SHA1Init(&ctxt);
+ SHA1Update(&ctxt, pn_secret, strlen(pn_secret));
+ strftime(tbuf, sizeof (tbuf), "%Y%m%d", tp);
+ SHA1Update(&ctxt, tbuf, strlen(tbuf));
+ SHA1Final(dig, &ctxt);
+ /* FIXME: if we want to do SRP, we need to find a way to pass the PolarSSL des_context instead of using static memory */
+ return (DesSetkey(dig));
+}
+
+static char base64[] =
+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+struct b64state {
+ u32_t bs_bits;
+ int bs_offs;
+};
+
+static int
+b64enc(bs, inp, inlen, outp)
+struct b64state *bs;
+u_char *inp;
+int inlen;
+u_char *outp;
+{
+ int outlen = 0;
+
+ while (inlen > 0) {
+ bs->bs_bits = (bs->bs_bits << 8) | *inp++;
+ inlen--;
+ bs->bs_offs += 8;
+ if (bs->bs_offs >= 24) {
+ *outp++ = base64[(bs->bs_bits >> 18) & 0x3F];
+ *outp++ = base64[(bs->bs_bits >> 12) & 0x3F];
+ *outp++ = base64[(bs->bs_bits >> 6) & 0x3F];
+ *outp++ = base64[bs->bs_bits & 0x3F];
+ outlen += 4;
+ bs->bs_offs = 0;
+ bs->bs_bits = 0;
+ }
+ }
+ return (outlen);
+}
+
+static int
+b64flush(bs, outp)
+struct b64state *bs;
+u_char *outp;
+{
+ int outlen = 0;
+
+ if (bs->bs_offs == 8) {
+ *outp++ = base64[(bs->bs_bits >> 2) & 0x3F];
+ *outp++ = base64[(bs->bs_bits << 4) & 0x3F];
+ outlen = 2;
+ } else if (bs->bs_offs == 16) {
+ *outp++ = base64[(bs->bs_bits >> 10) & 0x3F];
+ *outp++ = base64[(bs->bs_bits >> 4) & 0x3F];
+ *outp++ = base64[(bs->bs_bits << 2) & 0x3F];
+ outlen = 3;
+ }
+ bs->bs_offs = 0;
+ bs->bs_bits = 0;
+ return (outlen);
+}
+
+static int
+b64dec(bs, inp, inlen, outp)
+struct b64state *bs;
+u_char *inp;
+int inlen;
+u_char *outp;
+{
+ int outlen = 0;
+ char *cp;
+
+ while (inlen > 0) {
+ if ((cp = strchr(base64, *inp++)) == NULL)
+ break;
+ bs->bs_bits = (bs->bs_bits << 6) | (cp - base64);
+ inlen--;
+ bs->bs_offs += 6;
+ if (bs->bs_offs >= 8) {
+ *outp++ = bs->bs_bits >> (bs->bs_offs - 8);
+ outlen++;
+ bs->bs_offs -= 8;
+ }
+ }
+ return (outlen);
+}
+#endif /* USE_SRP */
+
+#if PPP_SERVER
+/*
+ * Assume that current waiting server state is complete and figure
+ * next state to use based on available authentication data. 'status'
+ * indicates if there was an error in handling the last query. It is
+ * 0 for success and non-zero for failure.
+ */
+static void eap_figure_next_state(ppp_pcb *pcb, int status) {
+#ifdef USE_SRP
+ unsigned char secbuf[MAXSECRETLEN], clear[8], *sp, *dp;
+ struct t_pw tpw;
+ struct t_confent *tce, mytce;
+ char *cp, *cp2;
+ struct t_server *ts;
+ int id, i, plen, toffs;
+ u_char vals[2];
+ struct b64state bs;
+#endif /* USE_SRP */
+
+ pcb->settings.eap_timeout_time = pcb->eap.es_savedtime;
+ switch (pcb->eap.es_server.ea_state) {
+ case eapBadAuth:
+ return;
+
+ case eapIdentify:
+#ifdef USE_SRP
+ /* Discard any previous session. */
+ ts = (struct t_server *)pcb->eap.es_server.ea_session;
+ if (ts != NULL) {
+ t_serverclose(ts);
+ pcb->eap.es_server.ea_session = NULL;
+ pcb->eap.es_server.ea_skey = NULL;
+ }
+#endif /* USE_SRP */
+ if (status != 0) {
+ pcb->eap.es_server.ea_state = eapBadAuth;
+ break;
+ }
+#ifdef USE_SRP
+ /* If we've got a pseudonym, try to decode to real name. */
+ if (pcb->eap.es_server.ea_peerlen > SRP_PSEUDO_LEN &&
+ strncmp(pcb->eap.es_server.ea_peer, SRP_PSEUDO_ID,
+ SRP_PSEUDO_LEN) == 0 &&
+ (pcb->eap.es_server.ea_peerlen - SRP_PSEUDO_LEN) * 3 / 4 <
+ sizeof (secbuf)) {
+ BZERO(&bs, sizeof (bs));
+ plen = b64dec(&bs,
+ pcb->eap.es_server.ea_peer + SRP_PSEUDO_LEN,
+ pcb->eap.es_server.ea_peerlen - SRP_PSEUDO_LEN,
+ secbuf);
+ toffs = 0;
+ for (i = 0; i < 5; i++) {
+ pncrypt_setkey(toffs);
+ toffs -= 86400;
+ /* FIXME: if we want to do SRP, we need to find a way to pass the PolarSSL des_context instead of using static memory */
+ if (!DesDecrypt(secbuf, clear)) {
+ ppp_dbglog("no DES here; cannot decode "
+ "pseudonym");
+ return;
+ }
+ id = *(unsigned char *)clear;
+ if (id + 1 <= plen && id + 9 > plen)
+ break;
+ }
+ if (plen % 8 == 0 && i < 5) {
+ /*
+ * Note that this is always shorter than the
+ * original stored string, so there's no need
+ * to realloc.
+ */
+ if ((i = plen = *(unsigned char *)clear) > 7)
+ i = 7;
+ pcb->eap.es_server.ea_peerlen = plen;
+ dp = (unsigned char *)pcb->eap.es_server.ea_peer;
+ MEMCPY(dp, clear + 1, i);
+ plen -= i;
+ dp += i;
+ sp = secbuf + 8;
+ while (plen > 0) {
+ /* FIXME: if we want to do SRP, we need to find a way to pass the PolarSSL des_context instead of using static memory */
+ (void) DesDecrypt(sp, dp);
+ sp += 8;
+ dp += 8;
+ plen -= 8;
+ }
+ pcb->eap.es_server.ea_peer[
+ pcb->eap.es_server.ea_peerlen] = '\0';
+ ppp_dbglog("decoded pseudonym to \"%.*q\"",
+ pcb->eap.es_server.ea_peerlen,
+ pcb->eap.es_server.ea_peer);
+ } else {
+ ppp_dbglog("failed to decode real name");
+ /* Stay in eapIdentfy state; requery */
+ break;
+ }
+ }
+ /* Look up user in secrets database. */
+ if (get_srp_secret(pcb->eap.es_unit, pcb->eap.es_server.ea_peer,
+ pcb->eap.es_server.ea_name, (char *)secbuf, 1) != 0) {
+ /* Set up default in case SRP entry is bad */
+ pcb->eap.es_server.ea_state = eapMD5Chall;
+ /* Get t_confent based on index in srp-secrets */
+ id = strtol((char *)secbuf, &cp, 10);
+ if (*cp++ != ':' || id < 0)
+ break;
+ if (id == 0) {
+ mytce.index = 0;
+ mytce.modulus.data = (u_char *)wkmodulus;
+ mytce.modulus.len = sizeof (wkmodulus);
+ mytce.generator.data = (u_char *)"\002";
+ mytce.generator.len = 1;
+ tce = &mytce;
+ } else if ((tce = gettcid(id)) != NULL) {
+ /*
+ * Client will have to verify this modulus/
+ * generator combination, and that will take
+ * a while. Lengthen the timeout here.
+ */
+ if (pcb->settings.eap_timeout_time > 0 &&
+ pcb->settings.eap_timeout_time < 30)
+ pcb->settings.eap_timeout_time = 30;
+ } else {
+ break;
+ }
+ if ((cp2 = strchr(cp, ':')) == NULL)
+ break;
+ *cp2++ = '\0';
+ tpw.pebuf.name = pcb->eap.es_server.ea_peer;
+ tpw.pebuf.password.len = t_fromb64((char *)tpw.pwbuf,
+ cp);
+ tpw.pebuf.password.data = tpw.pwbuf;
+ tpw.pebuf.salt.len = t_fromb64((char *)tpw.saltbuf,
+ cp2);
+ tpw.pebuf.salt.data = tpw.saltbuf;
+ if ((ts = t_serveropenraw(&tpw.pebuf, tce)) == NULL)
+ break;
+ pcb->eap.es_server.ea_session = (void *)ts;
+ pcb->eap.es_server.ea_state = eapSRP1;
+ vals[0] = pcb->eap.es_server.ea_id + 1;
+ vals[1] = EAPT_SRP;
+ t_serveraddexdata(ts, vals, 2);
+ /* Generate B; must call before t_servergetkey() */
+ t_servergenexp(ts);
+ break;
+ }
+#endif /* USE_SRP */
+ pcb->eap.es_server.ea_state = eapMD5Chall;
+ break;
+
+ case eapSRP1:
+#ifdef USE_SRP
+ ts = (struct t_server *)pcb->eap.es_server.ea_session;
+ if (ts != NULL && status != 0) {
+ t_serverclose(ts);
+ pcb->eap.es_server.ea_session = NULL;
+ pcb->eap.es_server.ea_skey = NULL;
+ }
+#endif /* USE_SRP */
+ if (status == 1) {
+ pcb->eap.es_server.ea_state = eapMD5Chall;
+ } else if (status != 0 || pcb->eap.es_server.ea_session == NULL) {
+ pcb->eap.es_server.ea_state = eapBadAuth;
+ } else {
+ pcb->eap.es_server.ea_state = eapSRP2;
+ }
+ break;
+
+ case eapSRP2:
+#ifdef USE_SRP
+ ts = (struct t_server *)pcb->eap.es_server.ea_session;
+ if (ts != NULL && status != 0) {
+ t_serverclose(ts);
+ pcb->eap.es_server.ea_session = NULL;
+ pcb->eap.es_server.ea_skey = NULL;
+ }
+#endif /* USE_SRP */
+ if (status != 0 || pcb->eap.es_server.ea_session == NULL) {
+ pcb->eap.es_server.ea_state = eapBadAuth;
+ } else {
+ pcb->eap.es_server.ea_state = eapSRP3;
+ }
+ break;
+
+ case eapSRP3:
+ case eapSRP4:
+#ifdef USE_SRP
+ ts = (struct t_server *)pcb->eap.es_server.ea_session;
+ if (ts != NULL && status != 0) {
+ t_serverclose(ts);
+ pcb->eap.es_server.ea_session = NULL;
+ pcb->eap.es_server.ea_skey = NULL;
+ }
+#endif /* USE_SRP */
+ if (status != 0 || pcb->eap.es_server.ea_session == NULL) {
+ pcb->eap.es_server.ea_state = eapBadAuth;
+ } else {
+ pcb->eap.es_server.ea_state = eapOpen;
+ }
+ break;
+
+ case eapMD5Chall:
+ if (status != 0) {
+ pcb->eap.es_server.ea_state = eapBadAuth;
+ } else {
+ pcb->eap.es_server.ea_state = eapOpen;
+ }
+ break;
+
+ default:
+ pcb->eap.es_server.ea_state = eapBadAuth;
+ break;
+ }
+ if (pcb->eap.es_server.ea_state == eapBadAuth)
+ eap_send_failure(pcb);
+}
+
+/*
+ * Format an EAP Request message and send it to the peer. Message
+ * type depends on current state. (Server operation)
+ */
+static void eap_send_request(ppp_pcb *pcb) {
+ struct pbuf *p;
+ u_char *outp;
+ u_char *lenloc;
+ int outlen;
+ int len;
+ const char *str;
+#ifdef USE_SRP
+ struct t_server *ts;
+ u_char clear[8], cipher[8], dig[SHA_DIGESTSIZE], *optr, *cp;
+ int i, j;
+ struct b64state b64;
+ SHA1_CTX ctxt;
+#endif /* USE_SRP */
+
+ /* Handle both initial auth and restart */
+ if (pcb->eap.es_server.ea_state < eapIdentify &&
+ pcb->eap.es_server.ea_state != eapInitial) {
+ pcb->eap.es_server.ea_state = eapIdentify;
+#if PPP_REMOTENAME
+ if (pcb->settings.explicit_remote && pcb->remote_name) {
+ /*
+ * If we already know the peer's
+ * unauthenticated name, then there's no
+ * reason to ask. Go to next state instead.
+ */
+ int len = (int)strlen(pcb->remote_name);
+ if (len > MAXNAMELEN) {
+ len = MAXNAMELEN;
+ }
+ MEMCPY(pcb->eap.es_server.ea_peer, pcb->remote_name, len);
+ pcb->eap.es_server.ea_peer[len] = '\0';
+ pcb->eap.es_server.ea_peerlen = len;
+ eap_figure_next_state(pcb, 0);
+ }
+#endif /* PPP_REMOTENAME */
+ }
+
+ if (pcb->settings.eap_max_transmits > 0 &&
+ pcb->eap.es_server.ea_requests >= pcb->settings.eap_max_transmits) {
+ if (pcb->eap.es_server.ea_responses > 0)
+ ppp_error("EAP: too many Requests sent");
+ else
+ ppp_error("EAP: no response to Requests");
+ eap_send_failure(pcb);
+ return;
+ }
+
+ p = pbuf_alloc(PBUF_RAW, (u16_t)(PPP_CTRL_PBUF_MAX_SIZE), PPP_CTRL_PBUF_TYPE);
+ if(NULL == p)
+ return;
+ if(p->tot_len != p->len) {
+ pbuf_free(p);
+ return;
+ }
+
+ outp = (u_char*)p->payload;
+
+ MAKEHEADER(outp, PPP_EAP);
+
+ PUTCHAR(EAP_REQUEST, outp);
+ PUTCHAR(pcb->eap.es_server.ea_id, outp);
+ lenloc = outp;
+ INCPTR(2, outp);
+
+ switch (pcb->eap.es_server.ea_state) {
+ case eapIdentify:
+ PUTCHAR(EAPT_IDENTITY, outp);
+ str = "Name";
+ len = strlen(str);
+ MEMCPY(outp, str, len);
+ INCPTR(len, outp);
+ break;
+
+ case eapMD5Chall:
+ PUTCHAR(EAPT_MD5CHAP, outp);
+ /*
+ * pick a random challenge length between
+ * EAP_MIN_CHALLENGE_LENGTH and EAP_MAX_CHALLENGE_LENGTH
+ */
+ pcb->eap.es_challen = EAP_MIN_CHALLENGE_LENGTH +
+ magic_pow(EAP_MIN_MAX_POWER_OF_TWO_CHALLENGE_LENGTH);
+ PUTCHAR(pcb->eap.es_challen, outp);
+ magic_random_bytes(pcb->eap.es_challenge, pcb->eap.es_challen);
+ MEMCPY(outp, pcb->eap.es_challenge, pcb->eap.es_challen);
+ INCPTR(pcb->eap.es_challen, outp);
+ MEMCPY(outp, pcb->eap.es_server.ea_name, pcb->eap.es_server.ea_namelen);
+ INCPTR(pcb->eap.es_server.ea_namelen, outp);
+ break;
+
+#ifdef USE_SRP
+ case eapSRP1:
+ PUTCHAR(EAPT_SRP, outp);
+ PUTCHAR(EAPSRP_CHALLENGE, outp);
+
+ PUTCHAR(pcb->eap.es_server.ea_namelen, outp);
+ MEMCPY(outp, pcb->eap.es_server.ea_name, pcb->eap.es_server.ea_namelen);
+ INCPTR(pcb->eap.es_server.ea_namelen, outp);
+
+ ts = (struct t_server *)pcb->eap.es_server.ea_session;
+ assert(ts != NULL);
+ PUTCHAR(ts->s.len, outp);
+ MEMCPY(outp, ts->s.data, ts->s.len);
+ INCPTR(ts->s.len, outp);
+
+ if (ts->g.len == 1 && ts->g.data[0] == 2) {
+ PUTCHAR(0, outp);
+ } else {
+ PUTCHAR(ts->g.len, outp);
+ MEMCPY(outp, ts->g.data, ts->g.len);
+ INCPTR(ts->g.len, outp);
+ }
+
+ if (ts->n.len != sizeof (wkmodulus) ||
+ BCMP(ts->n.data, wkmodulus, sizeof (wkmodulus)) != 0) {
+ MEMCPY(outp, ts->n.data, ts->n.len);
+ INCPTR(ts->n.len, outp);
+ }
+ break;
+
+ case eapSRP2:
+ PUTCHAR(EAPT_SRP, outp);
+ PUTCHAR(EAPSRP_SKEY, outp);
+
+ ts = (struct t_server *)pcb->eap.es_server.ea_session;
+ assert(ts != NULL);
+ MEMCPY(outp, ts->B.data, ts->B.len);
+ INCPTR(ts->B.len, outp);
+ break;
+
+ case eapSRP3:
+ PUTCHAR(EAPT_SRP, outp);
+ PUTCHAR(EAPSRP_SVALIDATOR, outp);
+ PUTLONG(SRPVAL_EBIT, outp);
+ ts = (struct t_server *)pcb->eap.es_server.ea_session;
+ assert(ts != NULL);
+ MEMCPY(outp, t_serverresponse(ts), SHA_DIGESTSIZE);
+ INCPTR(SHA_DIGESTSIZE, outp);
+
+ if (pncrypt_setkey(0)) {
+ /* Generate pseudonym */
+ optr = outp;
+ cp = (unsigned char *)pcb->eap.es_server.ea_peer;
+ if ((j = i = pcb->eap.es_server.ea_peerlen) > 7)
+ j = 7;
+ clear[0] = i;
+ MEMCPY(clear + 1, cp, j);
+ i -= j;
+ cp += j;
+ /* FIXME: if we want to do SRP, we need to find a way to pass the PolarSSL des_context instead of using static memory */
+ if (!DesEncrypt(clear, cipher)) {
+ ppp_dbglog("no DES here; not generating pseudonym");
+ break;
+ }
+ BZERO(&b64, sizeof (b64));
+ outp++; /* space for pseudonym length */
+ outp += b64enc(&b64, cipher, 8, outp);
+ while (i >= 8) {
+ /* FIXME: if we want to do SRP, we need to find a way to pass the PolarSSL des_context instead of using static memory */
+ (void) DesEncrypt(cp, cipher);
+ outp += b64enc(&b64, cipher, 8, outp);
+ cp += 8;
+ i -= 8;
+ }
+ if (i > 0) {
+ MEMCPY(clear, cp, i);
+ cp += i;
+ magic_random_bytes(cp, 8-i);
+ /* FIXME: if we want to do SRP, we need to find a way to pass the PolarSSL des_context instead of using static memory */
+ (void) DesEncrypt(clear, cipher);
+ outp += b64enc(&b64, cipher, 8, outp);
+ }
+ outp += b64flush(&b64, outp);
+
+ /* Set length and pad out to next 20 octet boundary */
+ i = outp - optr - 1;
+ *optr = i;
+ i %= SHA_DIGESTSIZE;
+ if (i != 0) {
+ magic_random_bytes(outp, SHA_DIGESTSIZE-i);
+ INCPTR(SHA_DIGESTSIZE-i, outp);
+ }
+
+ /* Obscure the pseudonym with SHA1 hash */
+ SHA1Init(&ctxt);
+ SHA1Update(&ctxt, &pcb->eap.es_server.ea_id, 1);
+ SHA1Update(&ctxt, pcb->eap.es_server.ea_skey,
+ SESSION_KEY_LEN);
+ SHA1Update(&ctxt, pcb->eap.es_server.ea_peer,
+ pcb->eap.es_server.ea_peerlen);
+ while (optr < outp) {
+ SHA1Final(dig, &ctxt);
+ cp = dig;
+ while (cp < dig + SHA_DIGESTSIZE)
+ *optr++ ^= *cp++;
+ SHA1Init(&ctxt);
+ SHA1Update(&ctxt, &pcb->eap.es_server.ea_id, 1);
+ SHA1Update(&ctxt, pcb->eap.es_server.ea_skey,
+ SESSION_KEY_LEN);
+ SHA1Update(&ctxt, optr - SHA_DIGESTSIZE,
+ SHA_DIGESTSIZE);
+ }
+ }
+ break;
+
+ case eapSRP4:
+ PUTCHAR(EAPT_SRP, outp);
+ PUTCHAR(EAPSRP_LWRECHALLENGE, outp);
+ pcb->eap.es_challen = EAP_MIN_CHALLENGE_LENGTH +
+ magic_pow(EAP_MIN_MAX_POWER_OF_TWO_CHALLENGE_LENGTH);
+ magic_random_bytes(pcb->eap.es_challenge, pcb->eap.es_challen);
+ MEMCPY(outp, pcb->eap.es_challenge, pcb->eap.es_challen);
+ INCPTR(pcb->eap.es_challen, outp);
+ break;
+#endif /* USE_SRP */
+
+ default:
+ return;
+ }
+
+ outlen = (outp - (unsigned char*)p->payload) - PPP_HDRLEN;
+ PUTSHORT(outlen, lenloc);
+
+ pbuf_realloc(p, outlen + PPP_HDRLEN);
+ ppp_write(pcb, p);
+
+ pcb->eap.es_server.ea_requests++;
+
+ if (pcb->settings.eap_timeout_time > 0)
+ TIMEOUT(eap_server_timeout, pcb, pcb->settings.eap_timeout_time);
+}
+
+/*
+ * eap_authpeer - Authenticate our peer (behave as server).
+ *
+ * Start server state and send first request. This is called only
+ * after eap_lowerup.
+ */
+void eap_authpeer(ppp_pcb *pcb, const char *localname) {
+
+ /* Save the name we're given. */
+ pcb->eap.es_server.ea_name = localname;
+ pcb->eap.es_server.ea_namelen = strlen(localname);
+
+ pcb->eap.es_savedtime = pcb->settings.eap_timeout_time;
+
+ /* Lower layer up yet? */
+ if (pcb->eap.es_server.ea_state == eapInitial ||
+ pcb->eap.es_server.ea_state == eapPending) {
+ pcb->eap.es_server.ea_state = eapPending;
+ return;
+ }
+
+ pcb->eap.es_server.ea_state = eapPending;
+
+ /* ID number not updated here intentionally; hashed into M1 */
+ eap_send_request(pcb);
+}
+
+/*
+ * eap_server_timeout - Retransmission timer for sending Requests
+ * expired.
+ */
+static void eap_server_timeout(void *arg) {
+ ppp_pcb *pcb = (ppp_pcb*)arg;
+
+ if (!eap_server_active(pcb))
+ return;
+
+ /* EAP ID number must not change on timeout. */
+ eap_send_request(pcb);
+}
+
+/*
+ * When it's time to send rechallenge the peer, this timeout is
+ * called. Once the rechallenge is successful, the response handler
+ * will restart the timer. If it fails, then the link is dropped.
+ */
+static void eap_rechallenge(void *arg) {
+ ppp_pcb *pcb = (ppp_pcb*)arg;
+
+ if (pcb->eap.es_server.ea_state != eapOpen &&
+ pcb->eap.es_server.ea_state != eapSRP4)
+ return;
+
+ pcb->eap.es_server.ea_requests = 0;
+ pcb->eap.es_server.ea_state = eapIdentify;
+ eap_figure_next_state(pcb, 0);
+ pcb->eap.es_server.ea_id++;
+ eap_send_request(pcb);
+}
+
+static void srp_lwrechallenge(void *arg) {
+ ppp_pcb *pcb = (ppp_pcb*)arg;
+
+ if (pcb->eap.es_server.ea_state != eapOpen ||
+ pcb->eap.es_server.ea_type != EAPT_SRP)
+ return;
+
+ pcb->eap.es_server.ea_requests = 0;
+ pcb->eap.es_server.ea_state = eapSRP4;
+ pcb->eap.es_server.ea_id++;
+ eap_send_request(pcb);
+}
+#endif /* PPP_SERVER */
+
+/*
+ * eap_lowerup - The lower layer is now up.
+ *
+ * This is called before either eap_authpeer or eap_authwithpeer. See
+ * link_established() in auth.c. All that's necessary here is to
+ * return to closed state so that those two routines will do the right
+ * thing.
+ */
+static void eap_lowerup(ppp_pcb *pcb) {
+ pcb->eap.es_client.ea_state = eapClosed;
+#if PPP_SERVER
+ pcb->eap.es_server.ea_state = eapClosed;
+#endif /* PPP_SERVER */
+}
+
+/*
+ * eap_lowerdown - The lower layer is now down.
+ *
+ * Cancel all timeouts and return to initial state.
+ */
+static void eap_lowerdown(ppp_pcb *pcb) {
+
+ if (eap_client_active(pcb) && pcb->settings.eap_req_time > 0) {
+ UNTIMEOUT(eap_client_timeout, pcb);
+ }
+#if PPP_SERVER
+ if (eap_server_active(pcb)) {
+ if (pcb->settings.eap_timeout_time > 0) {
+ UNTIMEOUT(eap_server_timeout, pcb);
+ }
+ } else {
+ if ((pcb->eap.es_server.ea_state == eapOpen ||
+ pcb->eap.es_server.ea_state == eapSRP4) &&
+ pcb->eap.es_rechallenge > 0) {
+ UNTIMEOUT(eap_rechallenge, (void *)pcb);
+ }
+ if (pcb->eap.es_server.ea_state == eapOpen &&
+ pcb->eap.es_lwrechallenge > 0) {
+ UNTIMEOUT(srp_lwrechallenge, (void *)pcb);
+ }
+ }
+
+ pcb->eap.es_client.ea_state = pcb->eap.es_server.ea_state = eapInitial;
+ pcb->eap.es_client.ea_requests = pcb->eap.es_server.ea_requests = 0;
+#endif /* PPP_SERVER */
+}
+
+/*
+ * eap_protrej - Peer doesn't speak this protocol.
+ *
+ * This shouldn't happen. If it does, it represents authentication
+ * failure.
+ */
+static void eap_protrej(ppp_pcb *pcb) {
+
+ if (eap_client_active(pcb)) {
+ ppp_error("EAP authentication failed due to Protocol-Reject");
+ auth_withpeer_fail(pcb, PPP_EAP);
+ }
+#if PPP_SERVER
+ if (eap_server_active(pcb)) {
+ ppp_error("EAP authentication of peer failed on Protocol-Reject");
+ auth_peer_fail(pcb, PPP_EAP);
+ }
+#endif /* PPP_SERVER */
+ eap_lowerdown(pcb);
+}
+
+/*
+ * Format and send a regular EAP Response message.
+ */
+static void eap_send_response(ppp_pcb *pcb, u_char id, u_char typenum, const u_char *str, int lenstr) {
+ struct pbuf *p;
+ u_char *outp;
+ int msglen;
+
+ msglen = EAP_HEADERLEN + sizeof (u_char) + lenstr;
+ p = pbuf_alloc(PBUF_RAW, (u16_t)(PPP_HDRLEN + msglen), PPP_CTRL_PBUF_TYPE);
+ if(NULL == p)
+ return;
+ if(p->tot_len != p->len) {
+ pbuf_free(p);
+ return;
+ }
+
+ outp = (u_char*)p->payload;
+
+ MAKEHEADER(outp, PPP_EAP);
+
+ PUTCHAR(EAP_RESPONSE, outp);
+ PUTCHAR(id, outp);
+ pcb->eap.es_client.ea_id = id;
+ PUTSHORT(msglen, outp);
+ PUTCHAR(typenum, outp);
+ if (lenstr > 0) {
+ MEMCPY(outp, str, lenstr);
+ }
+
+ ppp_write(pcb, p);
+}
+
+/*
+ * Format and send an MD5-Challenge EAP Response message.
+ */
+static void eap_chap_response(ppp_pcb *pcb, u_char id, u_char *hash, const char *name, int namelen) {
+ struct pbuf *p;
+ u_char *outp;
+ int msglen;
+
+ msglen = EAP_HEADERLEN + 2 * sizeof (u_char) + MD5_SIGNATURE_SIZE +
+ namelen;
+ p = pbuf_alloc(PBUF_RAW, (u16_t)(PPP_HDRLEN + msglen), PPP_CTRL_PBUF_TYPE);
+ if(NULL == p)
+ return;
+ if(p->tot_len != p->len) {
+ pbuf_free(p);
+ return;
+ }
+
+ outp = (u_char*)p->payload;
+
+ MAKEHEADER(outp, PPP_EAP);
+
+ PUTCHAR(EAP_RESPONSE, outp);
+ PUTCHAR(id, outp);
+ pcb->eap.es_client.ea_id = id;
+ PUTSHORT(msglen, outp);
+ PUTCHAR(EAPT_MD5CHAP, outp);
+ PUTCHAR(MD5_SIGNATURE_SIZE, outp);
+ MEMCPY(outp, hash, MD5_SIGNATURE_SIZE);
+ INCPTR(MD5_SIGNATURE_SIZE, outp);
+ if (namelen > 0) {
+ MEMCPY(outp, name, namelen);
+ }
+
+ ppp_write(pcb, p);
+}
+
+#ifdef USE_SRP
+/*
+ * Format and send a SRP EAP Response message.
+ */
+static void
+eap_srp_response(esp, id, subtypenum, str, lenstr)
+eap_state *esp;
+u_char id;
+u_char subtypenum;
+u_char *str;
+int lenstr;
+{
+ ppp_pcb *pcb = &ppp_pcb_list[pcb->eap.es_unit];
+ struct pbuf *p;
+ u_char *outp;
+ int msglen;
+
+ msglen = EAP_HEADERLEN + 2 * sizeof (u_char) + lenstr;
+ p = pbuf_alloc(PBUF_RAW, (u16_t)(PPP_HDRLEN + msglen), PPP_CTRL_PBUF_TYPE);
+ if(NULL == p)
+ return;
+ if(p->tot_len != p->len) {
+ pbuf_free(p);
+ return;
+ }
+
+ outp = p->payload;
+
+ MAKEHEADER(outp, PPP_EAP);
+
+ PUTCHAR(EAP_RESPONSE, outp);
+ PUTCHAR(id, outp);
+ pcb->eap.es_client.ea_id = id;
+ PUTSHORT(msglen, outp);
+ PUTCHAR(EAPT_SRP, outp);
+ PUTCHAR(subtypenum, outp);
+ if (lenstr > 0) {
+ MEMCPY(outp, str, lenstr);
+ }
+
+ ppp_write(pcb, p);
+}
+
+/*
+ * Format and send a SRP EAP Client Validator Response message.
+ */
+static void
+eap_srpval_response(esp, id, flags, str)
+eap_state *esp;
+u_char id;
+u32_t flags;
+u_char *str;
+{
+ ppp_pcb *pcb = &ppp_pcb_list[pcb->eap.es_unit];
+ struct pbuf *p;
+ u_char *outp;
+ int msglen;
+
+ msglen = EAP_HEADERLEN + 2 * sizeof (u_char) + sizeof (u32_t) +
+ SHA_DIGESTSIZE;
+ p = pbuf_alloc(PBUF_RAW, (u16_t)(PPP_HDRLEN + msglen), PPP_CTRL_PBUF_TYPE);
+ if(NULL == p)
+ return;
+ if(p->tot_len != p->len) {
+ pbuf_free(p);
+ return;
+ }
+
+ outp = p->payload;
+
+ MAKEHEADER(outp, PPP_EAP);
+
+ PUTCHAR(EAP_RESPONSE, outp);
+ PUTCHAR(id, outp);
+ pcb->eap.es_client.ea_id = id;
+ PUTSHORT(msglen, outp);
+ PUTCHAR(EAPT_SRP, outp);
+ PUTCHAR(EAPSRP_CVALIDATOR, outp);
+ PUTLONG(flags, outp);
+ MEMCPY(outp, str, SHA_DIGESTSIZE);
+
+ ppp_write(pcb, p);
+}
+#endif /* USE_SRP */
+
+static void eap_send_nak(ppp_pcb *pcb, u_char id, u_char type) {
+ struct pbuf *p;
+ u_char *outp;
+ int msglen;
+
+ msglen = EAP_HEADERLEN + 2 * sizeof (u_char);
+ p = pbuf_alloc(PBUF_RAW, (u16_t)(PPP_HDRLEN + msglen), PPP_CTRL_PBUF_TYPE);
+ if(NULL == p)
+ return;
+ if(p->tot_len != p->len) {
+ pbuf_free(p);
+ return;
+ }
+
+ outp = (u_char*)p->payload;
+
+ MAKEHEADER(outp, PPP_EAP);
+
+ PUTCHAR(EAP_RESPONSE, outp);
+ PUTCHAR(id, outp);
+ pcb->eap.es_client.ea_id = id;
+ PUTSHORT(msglen, outp);
+ PUTCHAR(EAPT_NAK, outp);
+ PUTCHAR(type, outp);
+
+ ppp_write(pcb, p);
+}
+
+#ifdef USE_SRP
+static char *
+name_of_pn_file()
+{
+ char *user, *path, *file;
+ struct passwd *pw;
+ size_t pl;
+ static bool pnlogged = 0;
+
+ pw = getpwuid(getuid());
+ if (pw == NULL || (user = pw->pw_dir) == NULL || user[0] == 0) {
+ errno = EINVAL;
+ return (NULL);
+ }
+ file = _PATH_PSEUDONYM;
+ pl = strlen(user) + strlen(file) + 2;
+ path = malloc(pl);
+ if (path == NULL)
+ return (NULL);
+ (void) slprintf(path, pl, "%s/%s", user, file);
+ if (!pnlogged) {
+ ppp_dbglog("pseudonym file: %s", path);
+ pnlogged = 1;
+ }
+ return (path);
+}
+
+static int
+open_pn_file(modebits)
+mode_t modebits;
+{
+ char *path;
+ int fd, err;
+
+ if ((path = name_of_pn_file()) == NULL)
+ return (-1);
+ fd = open(path, modebits, S_IRUSR | S_IWUSR);
+ err = errno;
+ free(path);
+ errno = err;
+ return (fd);
+}
+
+static void
+remove_pn_file()
+{
+ char *path;
+
+ if ((path = name_of_pn_file()) != NULL) {
+ (void) unlink(path);
+ (void) free(path);
+ }
+}
+
+static void
+write_pseudonym(esp, inp, len, id)
+eap_state *esp;
+u_char *inp;
+int len, id;
+{
+ u_char val;
+ u_char *datp, *digp;
+ SHA1_CTX ctxt;
+ u_char dig[SHA_DIGESTSIZE];
+ int dsize, fd, olen = len;
+
+ /*
+ * Do the decoding by working backwards. This eliminates the need
+ * to save the decoded output in a separate buffer.
+ */
+ val = id;
+ while (len > 0) {
+ if ((dsize = len % SHA_DIGESTSIZE) == 0)
+ dsize = SHA_DIGESTSIZE;
+ len -= dsize;
+ datp = inp + len;
+ SHA1Init(&ctxt);
+ SHA1Update(&ctxt, &val, 1);
+ SHA1Update(&ctxt, pcb->eap.es_client.ea_skey, SESSION_KEY_LEN);
+ if (len > 0) {
+ SHA1Update(&ctxt, datp, SHA_DIGESTSIZE);
+ } else {
+ SHA1Update(&ctxt, pcb->eap.es_client.ea_name,
+ pcb->eap.es_client.ea_namelen);
+ }
+ SHA1Final(dig, &ctxt);
+ for (digp = dig; digp < dig + SHA_DIGESTSIZE; digp++)
+ *datp++ ^= *digp;
+ }
+
+ /* Now check that the result is sane */
+ if (olen <= 0 || *inp + 1 > olen) {
+ ppp_dbglog("EAP: decoded pseudonym is unusable <%.*B>", olen, inp);
+ return;
+ }
+
+ /* Save it away */
+ fd = open_pn_file(O_WRONLY | O_CREAT | O_TRUNC);
+ if (fd < 0) {
+ ppp_dbglog("EAP: error saving pseudonym: %m");
+ return;
+ }
+ len = write(fd, inp + 1, *inp);
+ if (close(fd) != -1 && len == *inp) {
+ ppp_dbglog("EAP: saved pseudonym");
+ pcb->eap.es_usedpseudo = 0;
+ } else {
+ ppp_dbglog("EAP: failed to save pseudonym");
+ remove_pn_file();
+ }
+}
+#endif /* USE_SRP */
+
+/*
+ * eap_request - Receive EAP Request message (client mode).
+ */
+static void eap_request(ppp_pcb *pcb, u_char *inp, int id, int len) {
+ u_char typenum;
+ u_char vallen;
+ int secret_len;
+ char secret[MAXSECRETLEN];
+ char rhostname[MAXNAMELEN];
+ lwip_md5_context mdContext;
+ u_char hash[MD5_SIGNATURE_SIZE];
+#ifdef USE_SRP
+ struct t_client *tc;
+ struct t_num sval, gval, Nval, *Ap, Bval;
+ u_char vals[2];
+ SHA1_CTX ctxt;
+ u_char dig[SHA_DIGESTSIZE];
+ int fd;
+#endif /* USE_SRP */
+
+ /*
+ * Note: we update es_client.ea_id *only if* a Response
+ * message is being generated. Otherwise, we leave it the
+ * same for duplicate detection purposes.
+ */
+
+ pcb->eap.es_client.ea_requests++;
+ if (pcb->settings.eap_allow_req != 0 &&
+ pcb->eap.es_client.ea_requests > pcb->settings.eap_allow_req) {
+ ppp_info("EAP: received too many Request messages");
+ if (pcb->settings.eap_req_time > 0) {
+ UNTIMEOUT(eap_client_timeout, pcb);
+ }
+ auth_withpeer_fail(pcb, PPP_EAP);
+ return;
+ }
+
+ if (len <= 0) {
+ ppp_error("EAP: empty Request message discarded");
+ return;
+ }
+
+ GETCHAR(typenum, inp);
+ len--;
+
+ switch (typenum) {
+ case EAPT_IDENTITY:
+ if (len > 0)
+ ppp_info("EAP: Identity prompt \"%.*q\"", len, inp);
+#ifdef USE_SRP
+ if (pcb->eap.es_usepseudo &&
+ (pcb->eap.es_usedpseudo == 0 ||
+ (pcb->eap.es_usedpseudo == 1 &&
+ id == pcb->eap.es_client.ea_id))) {
+ pcb->eap.es_usedpseudo = 1;
+ /* Try to get a pseudonym */
+ if ((fd = open_pn_file(O_RDONLY)) >= 0) {
+ strcpy(rhostname, SRP_PSEUDO_ID);
+ len = read(fd, rhostname + SRP_PSEUDO_LEN,
+ sizeof (rhostname) - SRP_PSEUDO_LEN);
+ /* XXX NAI unsupported */
+ if (len > 0) {
+ eap_send_response(pcb, id, typenum,
+ rhostname, len + SRP_PSEUDO_LEN);
+ }
+ (void) close(fd);
+ if (len > 0)
+ break;
+ }
+ }
+ /* Stop using pseudonym now. */
+ if (pcb->eap.es_usepseudo && pcb->eap.es_usedpseudo != 2) {
+ remove_pn_file();
+ pcb->eap.es_usedpseudo = 2;
+ }
+#endif /* USE_SRP */
+ eap_send_response(pcb, id, typenum, (const u_char*)pcb->eap.es_client.ea_name,
+ pcb->eap.es_client.ea_namelen);
+ break;
+
+ case EAPT_NOTIFICATION:
+ if (len > 0)
+ ppp_info("EAP: Notification \"%.*q\"", len, inp);
+ eap_send_response(pcb, id, typenum, NULL, 0);
+ break;
+
+ case EAPT_NAK:
+ /*
+ * Avoid the temptation to send Response Nak in reply
+ * to Request Nak here. It can only lead to trouble.
+ */
+ ppp_warn("EAP: unexpected Nak in Request; ignored");
+ /* Return because we're waiting for something real. */
+ return;
+
+ case EAPT_MD5CHAP:
+ if (len < 1) {
+ ppp_error("EAP: received MD5-Challenge with no data");
+ /* Bogus request; wait for something real. */
+ return;
+ }
+ GETCHAR(vallen, inp);
+ len--;
+ if (vallen < 8 || vallen > len) {
+ ppp_error("EAP: MD5-Challenge with bad length %d (8..%d)",
+ vallen, len);
+ /* Try something better. */
+ eap_send_nak(pcb, id, EAPT_SRP);
+ break;
+ }
+
+ /* Not so likely to happen. */
+ if (vallen >= len + sizeof (rhostname)) {
+ ppp_dbglog("EAP: trimming really long peer name down");
+ MEMCPY(rhostname, inp + vallen, sizeof (rhostname) - 1);
+ rhostname[sizeof (rhostname) - 1] = '\0';
+ } else {
+ MEMCPY(rhostname, inp + vallen, len - vallen);
+ rhostname[len - vallen] = '\0';
+ }
+
+#if PPP_REMOTENAME
+ /* In case the remote doesn't give us his name. */
+ if (pcb->settings.explicit_remote ||
+ (pcb->settings.remote_name[0] != '\0' && vallen == len))
+ strlcpy(rhostname, pcb->settings.remote_name, sizeof (rhostname));
+#endif /* PPP_REMOTENAME */
+
+ /*
+ * Get the secret for authenticating ourselves with
+ * the specified host.
+ */
+ if (!get_secret(pcb, pcb->eap.es_client.ea_name,
+ rhostname, secret, &secret_len, 0)) {
+ ppp_dbglog("EAP: no MD5 secret for auth to %q", rhostname);
+ eap_send_nak(pcb, id, EAPT_SRP);
+ break;
+ }
+ lwip_md5_init(&mdContext);
+ lwip_md5_starts(&mdContext);
+ typenum = id;
+ lwip_md5_update(&mdContext, &typenum, 1);
+ lwip_md5_update(&mdContext, (u_char *)secret, secret_len);
+ BZERO(secret, sizeof (secret));
+ lwip_md5_update(&mdContext, inp, vallen);
+ lwip_md5_finish(&mdContext, hash);
+ lwip_md5_free(&mdContext);
+ eap_chap_response(pcb, id, hash, pcb->eap.es_client.ea_name,
+ pcb->eap.es_client.ea_namelen);
+ break;
+
+#ifdef USE_SRP
+ case EAPT_SRP:
+ if (len < 1) {
+ ppp_error("EAP: received empty SRP Request");
+ /* Bogus request; wait for something real. */
+ return;
+ }
+
+ /* Get subtype */
+ GETCHAR(vallen, inp);
+ len--;
+ switch (vallen) {
+ case EAPSRP_CHALLENGE:
+ tc = NULL;
+ if (pcb->eap.es_client.ea_session != NULL) {
+ tc = (struct t_client *)pcb->eap.es_client.
+ ea_session;
+ /*
+ * If this is a new challenge, then start
+ * over with a new client session context.
+ * Otherwise, just resend last response.
+ */
+ if (id != pcb->eap.es_client.ea_id) {
+ t_clientclose(tc);
+ pcb->eap.es_client.ea_session = NULL;
+ tc = NULL;
+ }
+ }
+ /* No session key just yet */
+ pcb->eap.es_client.ea_skey = NULL;
+ if (tc == NULL) {
+ int rhostnamelen;
+
+ GETCHAR(vallen, inp);
+ len--;
+ if (vallen >= len) {
+ ppp_error("EAP: badly-formed SRP Challenge"
+ " (name)");
+ /* Ignore badly-formed messages */
+ return;
+ }
+ MEMCPY(rhostname, inp, vallen);
+ rhostname[vallen] = '\0';
+ INCPTR(vallen, inp);
+ len -= vallen;
+
+ /*
+ * In case the remote doesn't give us his name,
+ * use configured name.
+ */
+ if (explicit_remote ||
+ (remote_name[0] != '\0' && vallen == 0)) {
+ strlcpy(rhostname, remote_name,
+ sizeof (rhostname));
+ }
+
+ rhostnamelen = (int)strlen(rhostname);
+ if (rhostnamelen > MAXNAMELEN) {
+ rhostnamelen = MAXNAMELEN;
+ }
+ MEMCPY(pcb->eap.es_client.ea_peer, rhostname, rhostnamelen);
+ pcb->eap.es_client.ea_peer[rhostnamelen] = '\0';
+ pcb->eap.es_client.ea_peerlen = rhostnamelen;
+
+ GETCHAR(vallen, inp);
+ len--;
+ if (vallen >= len) {
+ ppp_error("EAP: badly-formed SRP Challenge"
+ " (s)");
+ /* Ignore badly-formed messages */
+ return;
+ }
+ sval.data = inp;
+ sval.len = vallen;
+ INCPTR(vallen, inp);
+ len -= vallen;
+
+ GETCHAR(vallen, inp);
+ len--;
+ if (vallen > len) {
+ ppp_error("EAP: badly-formed SRP Challenge"
+ " (g)");
+ /* Ignore badly-formed messages */
+ return;
+ }
+ /* If no generator present, then use value 2 */
+ if (vallen == 0) {
+ gval.data = (u_char *)"\002";
+ gval.len = 1;
+ } else {
+ gval.data = inp;
+ gval.len = vallen;
+ }
+ INCPTR(vallen, inp);
+ len -= vallen;
+
+ /*
+ * If no modulus present, then use well-known
+ * value.
+ */
+ if (len == 0) {
+ Nval.data = (u_char *)wkmodulus;
+ Nval.len = sizeof (wkmodulus);
+ } else {
+ Nval.data = inp;
+ Nval.len = len;
+ }
+ tc = t_clientopen(pcb->eap.es_client.ea_name,
+ &Nval, &gval, &sval);
+ if (tc == NULL) {
+ eap_send_nak(pcb, id, EAPT_MD5CHAP);
+ break;
+ }
+ pcb->eap.es_client.ea_session = (void *)tc;
+
+ /* Add Challenge ID & type to verifier */
+ vals[0] = id;
+ vals[1] = EAPT_SRP;
+ t_clientaddexdata(tc, vals, 2);
+ }
+ Ap = t_clientgenexp(tc);
+ eap_srp_response(esp, id, EAPSRP_CKEY, Ap->data,
+ Ap->len);
+ break;
+
+ case EAPSRP_SKEY:
+ tc = (struct t_client *)pcb->eap.es_client.ea_session;
+ if (tc == NULL) {
+ ppp_warn("EAP: peer sent Subtype 2 without 1");
+ eap_send_nak(pcb, id, EAPT_MD5CHAP);
+ break;
+ }
+ if (pcb->eap.es_client.ea_skey != NULL) {
+ /*
+ * ID number should not change here. Warn
+ * if it does (but otherwise ignore).
+ */
+ if (id != pcb->eap.es_client.ea_id) {
+ ppp_warn("EAP: ID changed from %d to %d "
+ "in SRP Subtype 2 rexmit",
+ pcb->eap.es_client.ea_id, id);
+ }
+ } else {
+ if (get_srp_secret(pcb->eap.es_unit,
+ pcb->eap.es_client.ea_name,
+ pcb->eap.es_client.ea_peer, secret, 0) == 0) {
+ /*
+ * Can't work with this peer because
+ * the secret is missing. Just give
+ * up.
+ */
+ eap_send_nak(pcb, id, EAPT_MD5CHAP);
+ break;
+ }
+ Bval.data = inp;
+ Bval.len = len;
+ t_clientpasswd(tc, secret);
+ BZERO(secret, sizeof (secret));
+ pcb->eap.es_client.ea_skey =
+ t_clientgetkey(tc, &Bval);
+ if (pcb->eap.es_client.ea_skey == NULL) {
+ /* Server is rogue; stop now */
+ ppp_error("EAP: SRP server is rogue");
+ goto client_failure;
+ }
+ }
+ eap_srpval_response(esp, id, SRPVAL_EBIT,
+ t_clientresponse(tc));
+ break;
+
+ case EAPSRP_SVALIDATOR:
+ tc = (struct t_client *)pcb->eap.es_client.ea_session;
+ if (tc == NULL || pcb->eap.es_client.ea_skey == NULL) {
+ ppp_warn("EAP: peer sent Subtype 3 without 1/2");
+ eap_send_nak(pcb, id, EAPT_MD5CHAP);
+ break;
+ }
+ /*
+ * If we're already open, then this ought to be a
+ * duplicate. Otherwise, check that the server is
+ * who we think it is.
+ */
+ if (pcb->eap.es_client.ea_state == eapOpen) {
+ if (id != pcb->eap.es_client.ea_id) {
+ ppp_warn("EAP: ID changed from %d to %d "
+ "in SRP Subtype 3 rexmit",
+ pcb->eap.es_client.ea_id, id);
+ }
+ } else {
+ len -= sizeof (u32_t) + SHA_DIGESTSIZE;
+ if (len < 0 || t_clientverify(tc, inp +
+ sizeof (u32_t)) != 0) {
+ ppp_error("EAP: SRP server verification "
+ "failed");
+ goto client_failure;
+ }
+ GETLONG(pcb->eap.es_client.ea_keyflags, inp);
+ /* Save pseudonym if user wants it. */
+ if (len > 0 && pcb->eap.es_usepseudo) {
+ INCPTR(SHA_DIGESTSIZE, inp);
+ write_pseudonym(esp, inp, len, id);
+ }
+ }
+ /*
+ * We've verified our peer. We're now mostly done,
+ * except for waiting on the regular EAP Success
+ * message.
+ */
+ eap_srp_response(esp, id, EAPSRP_ACK, NULL, 0);
+ break;
+
+ case EAPSRP_LWRECHALLENGE:
+ if (len < 4) {
+ ppp_warn("EAP: malformed Lightweight rechallenge");
+ return;
+ }
+ SHA1Init(&ctxt);
+ vals[0] = id;
+ SHA1Update(&ctxt, vals, 1);
+ SHA1Update(&ctxt, pcb->eap.es_client.ea_skey,
+ SESSION_KEY_LEN);
+ SHA1Update(&ctxt, inp, len);
+ SHA1Update(&ctxt, pcb->eap.es_client.ea_name,
+ pcb->eap.es_client.ea_namelen);
+ SHA1Final(dig, &ctxt);
+ eap_srp_response(esp, id, EAPSRP_LWRECHALLENGE, dig,
+ SHA_DIGESTSIZE);
+ break;
+
+ default:
+ ppp_error("EAP: unknown SRP Subtype %d", vallen);
+ eap_send_nak(pcb, id, EAPT_MD5CHAP);
+ break;
+ }
+ break;
+#endif /* USE_SRP */
+
+ default:
+ ppp_info("EAP: unknown authentication type %d; Naking", typenum);
+ eap_send_nak(pcb, id, EAPT_SRP);
+ break;
+ }
+
+ if (pcb->settings.eap_req_time > 0) {
+ UNTIMEOUT(eap_client_timeout, pcb);
+ TIMEOUT(eap_client_timeout, pcb,
+ pcb->settings.eap_req_time);
+ }
+ return;
+
+#ifdef USE_SRP
+client_failure:
+ pcb->eap.es_client.ea_state = eapBadAuth;
+ if (pcb->settings.eap_req_time > 0) {
+ UNTIMEOUT(eap_client_timeout, (void *)esp);
+ }
+ pcb->eap.es_client.ea_session = NULL;
+ t_clientclose(tc);
+ auth_withpeer_fail(pcb, PPP_EAP);
+#endif /* USE_SRP */
+}
+
+#if PPP_SERVER
+/*
+ * eap_response - Receive EAP Response message (server mode).
+ */
+static void eap_response(ppp_pcb *pcb, u_char *inp, int id, int len) {
+ u_char typenum;
+ u_char vallen;
+ int secret_len;
+ char secret[MAXSECRETLEN];
+ char rhostname[MAXNAMELEN];
+ lwip_md5_context mdContext;
+ u_char hash[MD5_SIGNATURE_SIZE];
+#ifdef USE_SRP
+ struct t_server *ts;
+ struct t_num A;
+ SHA1_CTX ctxt;
+ u_char dig[SHA_DIGESTSIZE];
+#endif /* USE_SRP */
+
+ if (pcb->eap.es_server.ea_id != id) {
+ ppp_dbglog("EAP: discarding Response %d; expected ID %d", id,
+ pcb->eap.es_server.ea_id);
+ return;
+ }
+
+ pcb->eap.es_server.ea_responses++;
+
+ if (len <= 0) {
+ ppp_error("EAP: empty Response message discarded");
+ return;
+ }
+
+ GETCHAR(typenum, inp);
+ len--;
+
+ switch (typenum) {
+ case EAPT_IDENTITY:
+ if (pcb->eap.es_server.ea_state != eapIdentify) {
+ ppp_dbglog("EAP discarding unwanted Identify \"%.q\"", len,
+ inp);
+ break;
+ }
+ ppp_info("EAP: unauthenticated peer name \"%.*q\"", len, inp);
+ if (len > MAXNAMELEN) {
+ len = MAXNAMELEN;
+ }
+ MEMCPY(pcb->eap.es_server.ea_peer, inp, len);
+ pcb->eap.es_server.ea_peer[len] = '\0';
+ pcb->eap.es_server.ea_peerlen = len;
+ eap_figure_next_state(pcb, 0);
+ break;
+
+ case EAPT_NOTIFICATION:
+ ppp_dbglog("EAP unexpected Notification; response discarded");
+ break;
+
+ case EAPT_NAK:
+ if (len < 1) {
+ ppp_info("EAP: Nak Response with no suggested protocol");
+ eap_figure_next_state(pcb, 1);
+ break;
+ }
+
+ GETCHAR(vallen, inp);
+ len--;
+
+ if (
+#if PPP_REMOTENAME
+ !pcb->explicit_remote &&
+#endif /* PPP_REMOTENAME */
+ pcb->eap.es_server.ea_state == eapIdentify){
+ /* Peer cannot Nak Identify Request */
+ eap_figure_next_state(pcb, 1);
+ break;
+ }
+
+ switch (vallen) {
+ case EAPT_SRP:
+ /* Run through SRP validator selection again. */
+ pcb->eap.es_server.ea_state = eapIdentify;
+ eap_figure_next_state(pcb, 0);
+ break;
+
+ case EAPT_MD5CHAP:
+ pcb->eap.es_server.ea_state = eapMD5Chall;
+ break;
+
+ default:
+ ppp_dbglog("EAP: peer requesting unknown Type %d", vallen);
+ switch (pcb->eap.es_server.ea_state) {
+ case eapSRP1:
+ case eapSRP2:
+ case eapSRP3:
+ pcb->eap.es_server.ea_state = eapMD5Chall;
+ break;
+ case eapMD5Chall:
+ case eapSRP4:
+ pcb->eap.es_server.ea_state = eapIdentify;
+ eap_figure_next_state(pcb, 0);
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ break;
+
+ case EAPT_MD5CHAP:
+ if (pcb->eap.es_server.ea_state != eapMD5Chall) {
+ ppp_error("EAP: unexpected MD5-Response");
+ eap_figure_next_state(pcb, 1);
+ break;
+ }
+ if (len < 1) {
+ ppp_error("EAP: received MD5-Response with no data");
+ eap_figure_next_state(pcb, 1);
+ break;
+ }
+ GETCHAR(vallen, inp);
+ len--;
+ if (vallen != 16 || vallen > len) {
+ ppp_error("EAP: MD5-Response with bad length %d", vallen);
+ eap_figure_next_state(pcb, 1);
+ break;
+ }
+
+ /* Not so likely to happen. */
+ if (vallen >= len + sizeof (rhostname)) {
+ ppp_dbglog("EAP: trimming really long peer name down");
+ MEMCPY(rhostname, inp + vallen, sizeof (rhostname) - 1);
+ rhostname[sizeof (rhostname) - 1] = '\0';
+ } else {
+ MEMCPY(rhostname, inp + vallen, len - vallen);
+ rhostname[len - vallen] = '\0';
+ }
+
+#if PPP_REMOTENAME
+ /* In case the remote doesn't give us his name. */
+ if (explicit_remote ||
+ (remote_name[0] != '\0' && vallen == len))
+ strlcpy(rhostname, remote_name, sizeof (rhostname));
+#endif /* PPP_REMOTENAME */
+
+ /*
+ * Get the secret for authenticating the specified
+ * host.
+ */
+ if (!get_secret(pcb, rhostname,
+ pcb->eap.es_server.ea_name, secret, &secret_len, 1)) {
+ ppp_dbglog("EAP: no MD5 secret for auth of %q", rhostname);
+ eap_send_failure(pcb);
+ break;
+ }
+ lwip_md5_init(&mdContext);
+ lwip_md5_starts(&mdContext);
+ lwip_md5_update(&mdContext, &pcb->eap.es_server.ea_id, 1);
+ lwip_md5_update(&mdContext, (u_char *)secret, secret_len);
+ BZERO(secret, sizeof (secret));
+ lwip_md5_update(&mdContext, pcb->eap.es_challenge, pcb->eap.es_challen);
+ lwip_md5_finish(&mdContext, hash);
+ lwip_md5_free(&mdContext);
+ if (BCMP(hash, inp, MD5_SIGNATURE_SIZE) != 0) {
+ eap_send_failure(pcb);
+ break;
+ }
+ pcb->eap.es_server.ea_type = EAPT_MD5CHAP;
+ eap_send_success(pcb);
+ eap_figure_next_state(pcb, 0);
+ if (pcb->eap.es_rechallenge != 0)
+ TIMEOUT(eap_rechallenge, pcb, pcb->eap.es_rechallenge);
+ break;
+
+#ifdef USE_SRP
+ case EAPT_SRP:
+ if (len < 1) {
+ ppp_error("EAP: empty SRP Response");
+ eap_figure_next_state(pcb, 1);
+ break;
+ }
+ GETCHAR(typenum, inp);
+ len--;
+ switch (typenum) {
+ case EAPSRP_CKEY:
+ if (pcb->eap.es_server.ea_state != eapSRP1) {
+ ppp_error("EAP: unexpected SRP Subtype 1 Response");
+ eap_figure_next_state(pcb, 1);
+ break;
+ }
+ A.data = inp;
+ A.len = len;
+ ts = (struct t_server *)pcb->eap.es_server.ea_session;
+ assert(ts != NULL);
+ pcb->eap.es_server.ea_skey = t_servergetkey(ts, &A);
+ if (pcb->eap.es_server.ea_skey == NULL) {
+ /* Client's A value is bogus; terminate now */
+ ppp_error("EAP: bogus A value from client");
+ eap_send_failure(pcb);
+ } else {
+ eap_figure_next_state(pcb, 0);
+ }
+ break;
+
+ case EAPSRP_CVALIDATOR:
+ if (pcb->eap.es_server.ea_state != eapSRP2) {
+ ppp_error("EAP: unexpected SRP Subtype 2 Response");
+ eap_figure_next_state(pcb, 1);
+ break;
+ }
+ if (len < sizeof (u32_t) + SHA_DIGESTSIZE) {
+ ppp_error("EAP: M1 length %d < %d", len,
+ sizeof (u32_t) + SHA_DIGESTSIZE);
+ eap_figure_next_state(pcb, 1);
+ break;
+ }
+ GETLONG(pcb->eap.es_server.ea_keyflags, inp);
+ ts = (struct t_server *)pcb->eap.es_server.ea_session;
+ assert(ts != NULL);
+ if (t_serververify(ts, inp)) {
+ ppp_info("EAP: unable to validate client identity");
+ eap_send_failure(pcb);
+ break;
+ }
+ eap_figure_next_state(pcb, 0);
+ break;
+
+ case EAPSRP_ACK:
+ if (pcb->eap.es_server.ea_state != eapSRP3) {
+ ppp_error("EAP: unexpected SRP Subtype 3 Response");
+ eap_send_failure(esp);
+ break;
+ }
+ pcb->eap.es_server.ea_type = EAPT_SRP;
+ eap_send_success(pcb, esp);
+ eap_figure_next_state(pcb, 0);
+ if (pcb->eap.es_rechallenge != 0)
+ TIMEOUT(eap_rechallenge, pcb,
+ pcb->eap.es_rechallenge);
+ if (pcb->eap.es_lwrechallenge != 0)
+ TIMEOUT(srp_lwrechallenge, pcb,
+ pcb->eap.es_lwrechallenge);
+ break;
+
+ case EAPSRP_LWRECHALLENGE:
+ if (pcb->eap.es_server.ea_state != eapSRP4) {
+ ppp_info("EAP: unexpected SRP Subtype 4 Response");
+ return;
+ }
+ if (len != SHA_DIGESTSIZE) {
+ ppp_error("EAP: bad Lightweight rechallenge "
+ "response");
+ return;
+ }
+ SHA1Init(&ctxt);
+ vallen = id;
+ SHA1Update(&ctxt, &vallen, 1);
+ SHA1Update(&ctxt, pcb->eap.es_server.ea_skey,
+ SESSION_KEY_LEN);
+ SHA1Update(&ctxt, pcb->eap.es_challenge, pcb->eap.es_challen);
+ SHA1Update(&ctxt, pcb->eap.es_server.ea_peer,
+ pcb->eap.es_server.ea_peerlen);
+ SHA1Final(dig, &ctxt);
+ if (BCMP(dig, inp, SHA_DIGESTSIZE) != 0) {
+ ppp_error("EAP: failed Lightweight rechallenge");
+ eap_send_failure(pcb);
+ break;
+ }
+ pcb->eap.es_server.ea_state = eapOpen;
+ if (pcb->eap.es_lwrechallenge != 0)
+ TIMEOUT(srp_lwrechallenge, esp,
+ pcb->eap.es_lwrechallenge);
+ break;
+ }
+ break;
+#endif /* USE_SRP */
+
+ default:
+ /* This can't happen. */
+ ppp_error("EAP: unknown Response type %d; ignored", typenum);
+ return;
+ }
+
+ if (pcb->settings.eap_timeout_time > 0) {
+ UNTIMEOUT(eap_server_timeout, pcb);
+ }
+
+ if (pcb->eap.es_server.ea_state != eapBadAuth &&
+ pcb->eap.es_server.ea_state != eapOpen) {
+ pcb->eap.es_server.ea_id++;
+ eap_send_request(pcb);
+ }
+}
+#endif /* PPP_SERVER */
+
+/*
+ * eap_success - Receive EAP Success message (client mode).
+ */
+static void eap_success(ppp_pcb *pcb, u_char *inp, int id, int len) {
+ LWIP_UNUSED_ARG(id);
+
+ if (pcb->eap.es_client.ea_state != eapOpen && !eap_client_active(pcb)) {
+ ppp_dbglog("EAP unexpected success message in state %s (%d)",
+ eap_state_name(pcb->eap.es_client.ea_state),
+ pcb->eap.es_client.ea_state);
+ return;
+ }
+
+ if (pcb->settings.eap_req_time > 0) {
+ UNTIMEOUT(eap_client_timeout, pcb);
+ }
+
+ if (len > 0) {
+ /* This is odd. The spec doesn't allow for this. */
+ PRINTMSG(inp, len);
+ }
+
+ pcb->eap.es_client.ea_state = eapOpen;
+ auth_withpeer_success(pcb, PPP_EAP, 0);
+}
+
+/*
+ * eap_failure - Receive EAP Failure message (client mode).
+ */
+static void eap_failure(ppp_pcb *pcb, u_char *inp, int id, int len) {
+ LWIP_UNUSED_ARG(id);
+
+ if (!eap_client_active(pcb)) {
+ ppp_dbglog("EAP unexpected failure message in state %s (%d)",
+ eap_state_name(pcb->eap.es_client.ea_state),
+ pcb->eap.es_client.ea_state);
+ }
+
+ if (pcb->settings.eap_req_time > 0) {
+ UNTIMEOUT(eap_client_timeout, pcb);
+ }
+
+ if (len > 0) {
+ /* This is odd. The spec doesn't allow for this. */
+ PRINTMSG(inp, len);
+ }
+
+ pcb->eap.es_client.ea_state = eapBadAuth;
+
+ ppp_error("EAP: peer reports authentication failure");
+ auth_withpeer_fail(pcb, PPP_EAP);
+}
+
+/*
+ * eap_input - Handle received EAP message.
+ */
+static void eap_input(ppp_pcb *pcb, u_char *inp, int inlen) {
+ u_char code, id;
+ int len;
+
+ /*
+ * Parse header (code, id and length). If packet too short,
+ * drop it.
+ */
+ if (inlen < EAP_HEADERLEN) {
+ ppp_error("EAP: packet too short: %d < %d", inlen, EAP_HEADERLEN);
+ return;
+ }
+ GETCHAR(code, inp);
+ GETCHAR(id, inp);
+ GETSHORT(len, inp);
+ if (len < EAP_HEADERLEN || len > inlen) {
+ ppp_error("EAP: packet has illegal length field %d (%d..%d)", len,
+ EAP_HEADERLEN, inlen);
+ return;
+ }
+ len -= EAP_HEADERLEN;
+
+ /* Dispatch based on message code */
+ switch (code) {
+ case EAP_REQUEST:
+ eap_request(pcb, inp, id, len);
+ break;
+
+#if PPP_SERVER
+ case EAP_RESPONSE:
+ eap_response(pcb, inp, id, len);
+ break;
+#endif /* PPP_SERVER */
+
+ case EAP_SUCCESS:
+ eap_success(pcb, inp, id, len);
+ break;
+
+ case EAP_FAILURE:
+ eap_failure(pcb, inp, id, len);
+ break;
+
+ default: /* XXX Need code reject */
+ /* Note: it's not legal to send EAP Nak here. */
+ ppp_warn("EAP: unknown code %d received", code);
+ break;
+ }
+}
+
+#if PRINTPKT_SUPPORT
+/*
+ * eap_printpkt - print the contents of an EAP packet.
+ */
+static const char* const eap_codenames[] = {
+ "Request", "Response", "Success", "Failure"
+};
+
+static const char* const eap_typenames[] = {
+ "Identity", "Notification", "Nak", "MD5-Challenge",
+ "OTP", "Generic-Token", NULL, NULL,
+ "RSA", "DSS", "KEA", "KEA-Validate",
+ "TLS", "Defender", "Windows 2000", "Arcot",
+ "Cisco", "Nokia", "SRP"
+};
+
+static int eap_printpkt(const u_char *inp, int inlen, void (*printer) (void *, const char *, ...), void *arg) {
+ int code, id, len, rtype, vallen;
+ const u_char *pstart;
+ u32_t uval;
+
+ if (inlen < EAP_HEADERLEN)
+ return (0);
+ pstart = inp;
+ GETCHAR(code, inp);
+ GETCHAR(id, inp);
+ GETSHORT(len, inp);
+ if (len < EAP_HEADERLEN || len > inlen)
+ return (0);
+
+ if (code >= 1 && code <= (int)LWIP_ARRAYSIZE(eap_codenames))
+ printer(arg, " %s", eap_codenames[code-1]);
+ else
+ printer(arg, " code=0x%x", code);
+ printer(arg, " id=0x%x", id);
+ len -= EAP_HEADERLEN;
+ switch (code) {
+ case EAP_REQUEST:
+ if (len < 1) {
+ printer(arg, " <missing type>");
+ break;
+ }
+ GETCHAR(rtype, inp);
+ len--;
+ if (rtype >= 1 && rtype <= (int)LWIP_ARRAYSIZE(eap_typenames))
+ printer(arg, " %s", eap_typenames[rtype-1]);
+ else
+ printer(arg, " type=0x%x", rtype);
+ switch (rtype) {
+ case EAPT_IDENTITY:
+ case EAPT_NOTIFICATION:
+ if (len > 0) {
+ printer(arg, " <Message ");
+ ppp_print_string(inp, len, printer, arg);
+ printer(arg, ">");
+ INCPTR(len, inp);
+ len = 0;
+ } else {
+ printer(arg, " <No message>");
+ }
+ break;
+
+ case EAPT_MD5CHAP:
+ if (len <= 0)
+ break;
+ GETCHAR(vallen, inp);
+ len--;
+ if (vallen > len)
+ goto truncated;
+ printer(arg, " <Value%.*B>", vallen, inp);
+ INCPTR(vallen, inp);
+ len -= vallen;
+ if (len > 0) {
+ printer(arg, " <Name ");
+ ppp_print_string(inp, len, printer, arg);
+ printer(arg, ">");
+ INCPTR(len, inp);
+ len = 0;
+ } else {
+ printer(arg, " <No name>");
+ }
+ break;
+
+ case EAPT_SRP:
+ if (len < 3)
+ goto truncated;
+ GETCHAR(vallen, inp);
+ len--;
+ printer(arg, "-%d", vallen);
+ switch (vallen) {
+ case EAPSRP_CHALLENGE:
+ GETCHAR(vallen, inp);
+ len--;
+ if (vallen >= len)
+ goto truncated;
+ if (vallen > 0) {
+ printer(arg, " <Name ");
+ ppp_print_string(inp, vallen, printer,
+ arg);
+ printer(arg, ">");
+ } else {
+ printer(arg, " <No name>");
+ }
+ INCPTR(vallen, inp);
+ len -= vallen;
+ GETCHAR(vallen, inp);
+ len--;
+ if (vallen >= len)
+ goto truncated;
+ printer(arg, " <s%.*B>", vallen, inp);
+ INCPTR(vallen, inp);
+ len -= vallen;
+ GETCHAR(vallen, inp);
+ len--;
+ if (vallen > len)
+ goto truncated;
+ if (vallen == 0) {
+ printer(arg, " <Default g=2>");
+ } else {
+ printer(arg, " <g%.*B>", vallen, inp);
+ }
+ INCPTR(vallen, inp);
+ len -= vallen;
+ if (len == 0) {
+ printer(arg, " <Default N>");
+ } else {
+ printer(arg, " <N%.*B>", len, inp);
+ INCPTR(len, inp);
+ len = 0;
+ }
+ break;
+
+ case EAPSRP_SKEY:
+ printer(arg, " <B%.*B>", len, inp);
+ INCPTR(len, inp);
+ len = 0;
+ break;
+
+ case EAPSRP_SVALIDATOR:
+ if (len < (int)sizeof (u32_t))
+ break;
+ GETLONG(uval, inp);
+ len -= sizeof (u32_t);
+ if (uval & SRPVAL_EBIT) {
+ printer(arg, " E");
+ uval &= ~SRPVAL_EBIT;
+ }
+ if (uval != 0) {
+ printer(arg, " f<%X>", uval);
+ }
+ if ((vallen = len) > SHA_DIGESTSIZE)
+ vallen = SHA_DIGESTSIZE;
+ printer(arg, " <M2%.*B%s>", len, inp,
+ len < SHA_DIGESTSIZE ? "?" : "");
+ INCPTR(vallen, inp);
+ len -= vallen;
+ if (len > 0) {
+ printer(arg, " <PN%.*B>", len, inp);
+ INCPTR(len, inp);
+ len = 0;
+ }
+ break;
+
+ case EAPSRP_LWRECHALLENGE:
+ printer(arg, " <Challenge%.*B>", len, inp);
+ INCPTR(len, inp);
+ len = 0;
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case EAP_RESPONSE:
+ if (len < 1)
+ break;
+ GETCHAR(rtype, inp);
+ len--;
+ if (rtype >= 1 && rtype <= (int)LWIP_ARRAYSIZE(eap_typenames))
+ printer(arg, " %s", eap_typenames[rtype-1]);
+ else
+ printer(arg, " type=0x%x", rtype);
+ switch (rtype) {
+ case EAPT_IDENTITY:
+ if (len > 0) {
+ printer(arg, " <Name ");
+ ppp_print_string(inp, len, printer, arg);
+ printer(arg, ">");
+ INCPTR(len, inp);
+ len = 0;
+ }
+ break;
+
+ case EAPT_NAK:
+ if (len <= 0) {
+ printer(arg, " <missing hint>");
+ break;
+ }
+ GETCHAR(rtype, inp);
+ len--;
+ printer(arg, " <Suggested-type %02X", rtype);
+ if (rtype >= 1 && rtype < (int)LWIP_ARRAYSIZE(eap_typenames))
+ printer(arg, " (%s)", eap_typenames[rtype-1]);
+ printer(arg, ">");
+ break;
+
+ case EAPT_MD5CHAP:
+ if (len <= 0) {
+ printer(arg, " <missing length>");
+ break;
+ }
+ GETCHAR(vallen, inp);
+ len--;
+ if (vallen > len)
+ goto truncated;
+ printer(arg, " <Value%.*B>", vallen, inp);
+ INCPTR(vallen, inp);
+ len -= vallen;
+ if (len > 0) {
+ printer(arg, " <Name ");
+ ppp_print_string(inp, len, printer, arg);
+ printer(arg, ">");
+ INCPTR(len, inp);
+ len = 0;
+ } else {
+ printer(arg, " <No name>");
+ }
+ break;
+
+ case EAPT_SRP:
+ if (len < 1)
+ goto truncated;
+ GETCHAR(vallen, inp);
+ len--;
+ printer(arg, "-%d", vallen);
+ switch (vallen) {
+ case EAPSRP_CKEY:
+ printer(arg, " <A%.*B>", len, inp);
+ INCPTR(len, inp);
+ len = 0;
+ break;
+
+ case EAPSRP_CVALIDATOR:
+ if (len < (int)sizeof (u32_t))
+ break;
+ GETLONG(uval, inp);
+ len -= sizeof (u32_t);
+ if (uval & SRPVAL_EBIT) {
+ printer(arg, " E");
+ uval &= ~SRPVAL_EBIT;
+ }
+ if (uval != 0) {
+ printer(arg, " f<%X>", uval);
+ }
+ printer(arg, " <M1%.*B%s>", len, inp,
+ len == SHA_DIGESTSIZE ? "" : "?");
+ INCPTR(len, inp);
+ len = 0;
+ break;
+
+ case EAPSRP_ACK:
+ break;
+
+ case EAPSRP_LWRECHALLENGE:
+ printer(arg, " <Response%.*B%s>", len, inp,
+ len == SHA_DIGESTSIZE ? "" : "?");
+ if ((vallen = len) > SHA_DIGESTSIZE)
+ vallen = SHA_DIGESTSIZE;
+ INCPTR(vallen, inp);
+ len -= vallen;
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case EAP_SUCCESS: /* No payload expected for these! */
+ case EAP_FAILURE:
+ default:
+ break;
+
+ truncated:
+ printer(arg, " <truncated>");
+ break;
+ }
+
+ if (len > 8)
+ printer(arg, "%8B...", inp);
+ else if (len > 0)
+ printer(arg, "%.*B", len, inp);
+ INCPTR(len, inp);
+
+ return (inp - pstart);
+}
+#endif /* PRINTPKT_SUPPORT */
+
+#endif /* PPP_SUPPORT && EAP_SUPPORT */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/ecp.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/ecp.c
new file mode 100644
index 0000000..4d84f60
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/ecp.c
@@ -0,0 +1,191 @@
+/*
+ * ecp.c - PPP Encryption Control Protocol.
+ *
+ * Copyright (c) 2002 Google, Inc.
+ * 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(s) of the authors of this software must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission.
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Derived from ccp.c, which is:
+ *
+ * Copyright (c) 1994-2002 Paul Mackerras. 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. The name(s) of the authors of this software must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission.
+ *
+ * 3. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by Paul Mackerras
+ * <paulus@samba.org>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT && ECP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#include <string.h>
+
+#include "netif/ppp/ppp_impl.h"
+
+#include "netif/ppp/fsm.h"
+#include "netif/ppp/ecp.h"
+
+#if PPP_OPTIONS
+static option_t ecp_option_list[] = {
+ { "noecp", o_bool, &ecp_protent.enabled_flag,
+ "Disable ECP negotiation" },
+ { "-ecp", o_bool, &ecp_protent.enabled_flag,
+ "Disable ECP negotiation", OPT_ALIAS },
+
+ { NULL }
+};
+#endif /* PPP_OPTIONS */
+
+/*
+ * Protocol entry points from main code.
+ */
+static void ecp_init (int unit);
+/*
+static void ecp_open (int unit);
+static void ecp_close (int unit, char *);
+static void ecp_lowerup (int unit);
+static void ecp_lowerdown (int);
+static void ecp_input (int unit, u_char *pkt, int len);
+static void ecp_protrej (int unit);
+*/
+#if PRINTPKT_SUPPORT
+static int ecp_printpkt (const u_char *pkt, int len,
+ void (*printer) (void *, char *, ...),
+ void *arg);
+#endif /* PRINTPKT_SUPPORT */
+/*
+static void ecp_datainput (int unit, u_char *pkt, int len);
+*/
+
+const struct protent ecp_protent = {
+ PPP_ECP,
+ ecp_init,
+ NULL, /* ecp_input, */
+ NULL, /* ecp_protrej, */
+ NULL, /* ecp_lowerup, */
+ NULL, /* ecp_lowerdown, */
+ NULL, /* ecp_open, */
+ NULL, /* ecp_close, */
+#if PRINTPKT_SUPPORT
+ ecp_printpkt,
+#endif /* PRINTPKT_SUPPORT */
+#if PPP_DATAINPUT
+ NULL, /* ecp_datainput, */
+#endif /* PPP_DATAINPUT */
+#if PRINTPKT_SUPPORT
+ "ECP",
+ "Encrypted",
+#endif /* PRINTPKT_SUPPORT */
+#if PPP_OPTIONS
+ ecp_option_list,
+ NULL,
+#endif /* PPP_OPTIONS */
+#if DEMAND_SUPPORT
+ NULL,
+ NULL
+#endif /* DEMAND_SUPPORT */
+};
+
+fsm ecp_fsm[NUM_PPP];
+ecp_options ecp_wantoptions[NUM_PPP]; /* what to request the peer to use */
+ecp_options ecp_gotoptions[NUM_PPP]; /* what the peer agreed to do */
+ecp_options ecp_allowoptions[NUM_PPP]; /* what we'll agree to do */
+ecp_options ecp_hisoptions[NUM_PPP]; /* what we agreed to do */
+
+static const fsm_callbacks ecp_callbacks = {
+ NULL, /* ecp_resetci, */
+ NULL, /* ecp_cilen, */
+ NULL, /* ecp_addci, */
+ NULL, /* ecp_ackci, */
+ NULL, /* ecp_nakci, */
+ NULL, /* ecp_rejci, */
+ NULL, /* ecp_reqci, */
+ NULL, /* ecp_up, */
+ NULL, /* ecp_down, */
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL, /* ecp_extcode, */
+ "ECP"
+};
+
+/*
+ * ecp_init - initialize ECP.
+ */
+static void
+ecp_init(unit)
+ int unit;
+{
+ fsm *f = &ecp_fsm[unit];
+
+ f->unit = unit;
+ f->protocol = PPP_ECP;
+ f->callbacks = &ecp_callbacks;
+ fsm_init(f);
+
+#if 0 /* Not necessary, everything is cleared in ppp_new() */
+ memset(&ecp_wantoptions[unit], 0, sizeof(ecp_options));
+ memset(&ecp_gotoptions[unit], 0, sizeof(ecp_options));
+ memset(&ecp_allowoptions[unit], 0, sizeof(ecp_options));
+ memset(&ecp_hisoptions[unit], 0, sizeof(ecp_options));
+#endif /* 0 */
+
+}
+
+
+#if PRINTPKT_SUPPORT
+static int
+ecp_printpkt(p, plen, printer, arg)
+ const u_char *p;
+ int plen;
+ void (*printer) (void *, char *, ...);
+ void *arg;
+{
+ return 0;
+}
+#endif /* PRINTPKT_SUPPORT */
+
+#endif /* PPP_SUPPORT && ECP_SUPPORT */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/eui64.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/eui64.c
new file mode 100644
index 0000000..01493bc
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/eui64.c
@@ -0,0 +1,56 @@
+/*
+ * eui64.c - EUI64 routines for IPv6CP.
+ *
+ * Copyright (c) 1999 Tommi Komulainen. 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(s) of the authors of this software must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission.
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by Tommi Komulainen
+ * <Tommi.Komulainen@iki.fi>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $Id: eui64.c,v 1.6 2002/12/04 23:03:32 paulus Exp $
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT && PPP_IPV6_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#include "netif/ppp/ppp_impl.h"
+#include "netif/ppp/eui64.h"
+
+/*
+ * eui64_ntoa - Make an ascii representation of an interface identifier
+ */
+char *eui64_ntoa(eui64_t e) {
+ static char buf[20];
+
+ sprintf(buf, "%02x%02x:%02x%02x:%02x%02x:%02x%02x",
+ e.e8[0], e.e8[1], e.e8[2], e.e8[3],
+ e.e8[4], e.e8[5], e.e8[6], e.e8[7]);
+ return buf;
+}
+
+#endif /* PPP_SUPPORT && PPP_IPV6_SUPPORT */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/fsm.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/fsm.c
new file mode 100644
index 0000000..81eba11
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/fsm.c
@@ -0,0 +1,799 @@
+/*
+ * fsm.c - {Link, IP} Control Protocol Finite State Machine.
+ *
+ * Copyright (c) 1984-2000 Carnegie Mellon University. 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 "Carnegie Mellon University" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission. For permission or any legal
+ * details, please contact
+ * Office of Technology Transfer
+ * Carnegie Mellon University
+ * 5000 Forbes Avenue
+ * Pittsburgh, PA 15213-3890
+ * (412) 268-4387, fax: (412) 268-7395
+ * tech-transfer@andrew.cmu.edu
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by Computing Services
+ * at Carnegie Mellon University (http://www.cmu.edu/computing/)."
+ *
+ * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
+ * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+/*
+ * @todo:
+ * Randomize fsm id on link/init.
+ * Deal with variable outgoing MTU.
+ */
+
+#if 0 /* UNUSED */
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#endif /* UNUSED */
+
+#include "netif/ppp/ppp_impl.h"
+
+#include "netif/ppp/fsm.h"
+
+static void fsm_timeout (void *);
+static void fsm_rconfreq(fsm *f, u_char id, u_char *inp, int len);
+static void fsm_rconfack(fsm *f, int id, u_char *inp, int len);
+static void fsm_rconfnakrej(fsm *f, int code, int id, u_char *inp, int len);
+static void fsm_rtermreq(fsm *f, int id, u_char *p, int len);
+static void fsm_rtermack(fsm *f);
+static void fsm_rcoderej(fsm *f, u_char *inp, int len);
+static void fsm_sconfreq(fsm *f, int retransmit);
+
+#define PROTO_NAME(f) ((f)->callbacks->proto_name)
+
+/*
+ * fsm_init - Initialize fsm.
+ *
+ * Initialize fsm state.
+ */
+void fsm_init(fsm *f) {
+ ppp_pcb *pcb = f->pcb;
+ f->state = PPP_FSM_INITIAL;
+ f->flags = 0;
+ f->id = 0; /* XXX Start with random id? */
+ f->maxnakloops = pcb->settings.fsm_max_nak_loops;
+ f->term_reason_len = 0;
+}
+
+
+/*
+ * fsm_lowerup - The lower layer is up.
+ */
+void fsm_lowerup(fsm *f) {
+ switch( f->state ){
+ case PPP_FSM_INITIAL:
+ f->state = PPP_FSM_CLOSED;
+ break;
+
+ case PPP_FSM_STARTING:
+ if( f->flags & OPT_SILENT )
+ f->state = PPP_FSM_STOPPED;
+ else {
+ /* Send an initial configure-request */
+ fsm_sconfreq(f, 0);
+ f->state = PPP_FSM_REQSENT;
+ }
+ break;
+
+ default:
+ FSMDEBUG(("%s: Up event in state %d!", PROTO_NAME(f), f->state));
+ /* no break */
+ }
+}
+
+
+/*
+ * fsm_lowerdown - The lower layer is down.
+ *
+ * Cancel all timeouts and inform upper layers.
+ */
+void fsm_lowerdown(fsm *f) {
+ switch( f->state ){
+ case PPP_FSM_CLOSED:
+ f->state = PPP_FSM_INITIAL;
+ break;
+
+ case PPP_FSM_STOPPED:
+ f->state = PPP_FSM_STARTING;
+ if( f->callbacks->starting )
+ (*f->callbacks->starting)(f);
+ break;
+
+ case PPP_FSM_CLOSING:
+ f->state = PPP_FSM_INITIAL;
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ break;
+
+ case PPP_FSM_STOPPING:
+ case PPP_FSM_REQSENT:
+ case PPP_FSM_ACKRCVD:
+ case PPP_FSM_ACKSENT:
+ f->state = PPP_FSM_STARTING;
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ break;
+
+ case PPP_FSM_OPENED:
+ if( f->callbacks->down )
+ (*f->callbacks->down)(f);
+ f->state = PPP_FSM_STARTING;
+ break;
+
+ default:
+ FSMDEBUG(("%s: Down event in state %d!", PROTO_NAME(f), f->state));
+ /* no break */
+ }
+}
+
+
+/*
+ * fsm_open - Link is allowed to come up.
+ */
+void fsm_open(fsm *f) {
+ switch( f->state ){
+ case PPP_FSM_INITIAL:
+ f->state = PPP_FSM_STARTING;
+ if( f->callbacks->starting )
+ (*f->callbacks->starting)(f);
+ break;
+
+ case PPP_FSM_CLOSED:
+ if( f->flags & OPT_SILENT )
+ f->state = PPP_FSM_STOPPED;
+ else {
+ /* Send an initial configure-request */
+ fsm_sconfreq(f, 0);
+ f->state = PPP_FSM_REQSENT;
+ }
+ break;
+
+ case PPP_FSM_CLOSING:
+ f->state = PPP_FSM_STOPPING;
+ /* fall through */
+ /* no break */
+ case PPP_FSM_STOPPED:
+ case PPP_FSM_OPENED:
+ if( f->flags & OPT_RESTART ){
+ fsm_lowerdown(f);
+ fsm_lowerup(f);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+/*
+ * 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) {
+ ppp_pcb *pcb = f->pcb;
+
+ if( f->state != PPP_FSM_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 and send Terminate-Request */
+ f->retransmits = pcb->settings.fsm_max_term_transmits;
+ fsm_sdata(f, TERMREQ, f->reqid = ++f->id,
+ (const u_char *) f->term_reason, f->term_reason_len);
+
+ if (f->retransmits == 0) {
+ /*
+ * User asked for no terminate requests at all; just close it.
+ * We've already fired off one Terminate-Request just to be nice
+ * to the peer, but we're not going to wait for a reply.
+ */
+ f->state = nextstate == PPP_FSM_CLOSING ? PPP_FSM_CLOSED : PPP_FSM_STOPPED;
+ if( f->callbacks->finished )
+ (*f->callbacks->finished)(f);
+ return;
+ }
+
+ TIMEOUT(fsm_timeout, f, pcb->settings.fsm_timeout_time);
+ --f->retransmits;
+
+ f->state = nextstate;
+}
+
+/*
+ * fsm_close - Start closing connection.
+ *
+ * Cancel timeouts and either initiate close or possibly go directly to
+ * the PPP_FSM_CLOSED state.
+ */
+void fsm_close(fsm *f, const char *reason) {
+ f->term_reason = reason;
+ f->term_reason_len = (reason == NULL? 0: LWIP_MIN(strlen(reason), 0xFF) );
+ switch( f->state ){
+ case PPP_FSM_STARTING:
+ f->state = PPP_FSM_INITIAL;
+ break;
+ case PPP_FSM_STOPPED:
+ f->state = PPP_FSM_CLOSED;
+ break;
+ case PPP_FSM_STOPPING:
+ f->state = PPP_FSM_CLOSING;
+ break;
+
+ case PPP_FSM_REQSENT:
+ case PPP_FSM_ACKRCVD:
+ case PPP_FSM_ACKSENT:
+ case PPP_FSM_OPENED:
+ terminate_layer(f, PPP_FSM_CLOSING);
+ break;
+ default:
+ break;
+ }
+}
+
+
+/*
+ * fsm_timeout - Timeout expired.
+ */
+static void fsm_timeout(void *arg) {
+ fsm *f = (fsm *) arg;
+ ppp_pcb *pcb = f->pcb;
+
+ switch (f->state) {
+ case PPP_FSM_CLOSING:
+ case PPP_FSM_STOPPING:
+ if( f->retransmits <= 0 ){
+ /*
+ * We've waited for an ack long enough. Peer probably heard us.
+ */
+ f->state = (f->state == PPP_FSM_CLOSING)? PPP_FSM_CLOSED: PPP_FSM_STOPPED;
+ if( f->callbacks->finished )
+ (*f->callbacks->finished)(f);
+ } else {
+ /* Send Terminate-Request */
+ fsm_sdata(f, TERMREQ, f->reqid = ++f->id,
+ (const u_char *) f->term_reason, f->term_reason_len);
+ TIMEOUT(fsm_timeout, f, pcb->settings.fsm_timeout_time);
+ --f->retransmits;
+ }
+ break;
+
+ case PPP_FSM_REQSENT:
+ case PPP_FSM_ACKRCVD:
+ case PPP_FSM_ACKSENT:
+ if (f->retransmits <= 0) {
+ ppp_warn("%s: timeout sending Config-Requests", PROTO_NAME(f));
+ f->state = PPP_FSM_STOPPED;
+ if( (f->flags & OPT_PASSIVE) == 0 && f->callbacks->finished )
+ (*f->callbacks->finished)(f);
+
+ } else {
+ /* Retransmit the configure-request */
+ if (f->callbacks->retransmit)
+ (*f->callbacks->retransmit)(f);
+ fsm_sconfreq(f, 1); /* Re-send Configure-Request */
+ if( f->state == PPP_FSM_ACKRCVD )
+ f->state = PPP_FSM_REQSENT;
+ }
+ break;
+
+ default:
+ FSMDEBUG(("%s: Timeout event in state %d!", PROTO_NAME(f), f->state));
+ /* no break */
+ }
+}
+
+
+/*
+ * fsm_input - Input packet.
+ */
+void fsm_input(fsm *f, u_char *inpacket, int l) {
+ 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 < HEADERLEN) {
+ FSMDEBUG(("fsm_input(%x): Rcvd short header.", f->protocol));
+ return;
+ }
+ GETCHAR(code, inp);
+ GETCHAR(id, inp);
+ GETSHORT(len, inp);
+ if (len < HEADERLEN) {
+ FSMDEBUG(("fsm_input(%x): Rcvd illegal length.", f->protocol));
+ return;
+ }
+ if (len > l) {
+ FSMDEBUG(("fsm_input(%x): Rcvd short packet.", f->protocol));
+ return;
+ }
+ len -= HEADERLEN; /* subtract header length */
+
+ if( f->state == PPP_FSM_INITIAL || f->state == PPP_FSM_STARTING ){
+ FSMDEBUG(("fsm_input(%x): Rcvd packet in state %d.",
+ f->protocol, f->state));
+ return;
+ }
+
+ /*
+ * 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:
+ 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;
+
+ switch( f->state ){
+ case PPP_FSM_CLOSED:
+ /* Go away, we're closed */
+ fsm_sdata(f, TERMACK, id, NULL, 0);
+ return;
+ case PPP_FSM_CLOSING:
+ case PPP_FSM_STOPPING:
+ return;
+
+ case PPP_FSM_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 = PPP_FSM_REQSENT;
+ break;
+
+ case PPP_FSM_STOPPED:
+ /* Negotiation started by our peer */
+ fsm_sconfreq(f, 0); /* Send initial Configure-Request */
+ f->state = PPP_FSM_REQSENT;
+ break;
+ default:
+ 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, code, id, inp, len);
+
+ if (code == CONFACK) {
+ if (f->state == PPP_FSM_ACKRCVD) {
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ f->state = PPP_FSM_OPENED;
+ if (f->callbacks->up)
+ (*f->callbacks->up)(f); /* Inform upper layers */
+ } else
+ f->state = PPP_FSM_ACKSENT;
+ f->nakloops = 0;
+
+ } else {
+ /* we sent CONFACK or CONFREJ */
+ if (f->state != PPP_FSM_ACKRCVD)
+ f->state = PPP_FSM_REQSENT;
+ if( code == CONFNAK )
+ ++f->nakloops;
+ }
+}
+
+
+/*
+ * fsm_rconfack - Receive Configure-Ack.
+ */
+static void fsm_rconfack(fsm *f, int id, u_char *inp, int len) {
+ ppp_pcb *pcb = f->pcb;
+
+ 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 */
+ ppp_error("Received bad configure-ack: %P", inp, len);
+ return;
+ }
+ f->seen_ack = 1;
+ f->rnakloops = 0;
+
+ switch (f->state) {
+ case PPP_FSM_CLOSED:
+ case PPP_FSM_STOPPED:
+ fsm_sdata(f, TERMACK, id, NULL, 0);
+ break;
+
+ case PPP_FSM_REQSENT:
+ f->state = PPP_FSM_ACKRCVD;
+ f->retransmits = pcb->settings.fsm_max_conf_req_transmits;
+ break;
+
+ case PPP_FSM_ACKRCVD:
+ /* Huh? an extra valid Ack? oh well... */
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ fsm_sconfreq(f, 0);
+ f->state = PPP_FSM_REQSENT;
+ break;
+
+ case PPP_FSM_ACKSENT:
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ f->state = PPP_FSM_OPENED;
+ f->retransmits = pcb->settings.fsm_max_conf_req_transmits;
+ if (f->callbacks->up)
+ (*f->callbacks->up)(f); /* Inform upper layers */
+ break;
+
+ case PPP_FSM_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 = PPP_FSM_REQSENT;
+ break;
+ default:
+ 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 ret;
+ int treat_as_reject;
+
+ if (id != f->reqid || f->seen_ack) /* Expected id? */
+ return; /* Nope, toss... */
+
+ if (code == CONFNAK) {
+ ++f->rnakloops;
+ treat_as_reject = (f->rnakloops >= f->maxnakloops);
+ if (f->callbacks->nakci == NULL
+ || !(ret = f->callbacks->nakci(f, inp, len, treat_as_reject))) {
+ ppp_error("Received bad configure-nak: %P", inp, len);
+ return;
+ }
+ } else {
+ f->rnakloops = 0;
+ if (f->callbacks->rejci == NULL
+ || !(ret = f->callbacks->rejci(f, inp, len))) {
+ ppp_error("Received bad configure-rej: %P", inp, len);
+ return;
+ }
+ }
+
+ f->seen_ack = 1;
+
+ switch (f->state) {
+ case PPP_FSM_CLOSED:
+ case PPP_FSM_STOPPED:
+ fsm_sdata(f, TERMACK, id, NULL, 0);
+ break;
+
+ case PPP_FSM_REQSENT:
+ case PPP_FSM_ACKSENT:
+ /* They didn't agree to what we wanted - try another request */
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ if (ret < 0)
+ f->state = PPP_FSM_STOPPED; /* kludge for stopping CCP */
+ else
+ fsm_sconfreq(f, 0); /* Send Configure-Request */
+ break;
+
+ case PPP_FSM_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 = PPP_FSM_REQSENT;
+ break;
+
+ case PPP_FSM_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 = PPP_FSM_REQSENT;
+ break;
+ default:
+ break;
+ }
+}
+
+
+/*
+ * fsm_rtermreq - Receive Terminate-Req.
+ */
+static void fsm_rtermreq(fsm *f, int id, u_char *p, int len) {
+ ppp_pcb *pcb = f->pcb;
+
+ switch (f->state) {
+ case PPP_FSM_ACKRCVD:
+ case PPP_FSM_ACKSENT:
+ f->state = PPP_FSM_REQSENT; /* Start over but keep trying */
+ break;
+
+ case PPP_FSM_OPENED:
+ if (len > 0) {
+ ppp_info("%s terminated by peer (%0.*v)", PROTO_NAME(f), len, p);
+ } else
+ ppp_info("%s terminated by peer", PROTO_NAME(f));
+ f->retransmits = 0;
+ f->state = PPP_FSM_STOPPING;
+ if (f->callbacks->down)
+ (*f->callbacks->down)(f); /* Inform upper layers */
+ TIMEOUT(fsm_timeout, f, pcb->settings.fsm_timeout_time);
+ break;
+ default:
+ break;
+ }
+
+ fsm_sdata(f, TERMACK, id, NULL, 0);
+}
+
+
+/*
+ * fsm_rtermack - Receive Terminate-Ack.
+ */
+static void fsm_rtermack(fsm *f) {
+ switch (f->state) {
+ case PPP_FSM_CLOSING:
+ UNTIMEOUT(fsm_timeout, f);
+ f->state = PPP_FSM_CLOSED;
+ if( f->callbacks->finished )
+ (*f->callbacks->finished)(f);
+ break;
+ case PPP_FSM_STOPPING:
+ UNTIMEOUT(fsm_timeout, f);
+ f->state = PPP_FSM_STOPPED;
+ if( f->callbacks->finished )
+ (*f->callbacks->finished)(f);
+ break;
+
+ case PPP_FSM_ACKRCVD:
+ f->state = PPP_FSM_REQSENT;
+ break;
+
+ case PPP_FSM_OPENED:
+ if (f->callbacks->down)
+ (*f->callbacks->down)(f); /* Inform upper layers */
+ fsm_sconfreq(f, 0);
+ f->state = PPP_FSM_REQSENT;
+ break;
+ default:
+ break;
+ }
+}
+
+
+/*
+ * fsm_rcoderej - Receive an Code-Reject.
+ */
+static void fsm_rcoderej(fsm *f, u_char *inp, int len) {
+ u_char code, id;
+
+ if (len < HEADERLEN) {
+ FSMDEBUG(("fsm_rcoderej: Rcvd short Code-Reject packet!"));
+ return;
+ }
+ GETCHAR(code, inp);
+ GETCHAR(id, inp);
+ ppp_warn("%s: Rcvd Code-Reject for code %d, id %d", PROTO_NAME(f), code, id);
+
+ if( f->state == PPP_FSM_ACKRCVD )
+ f->state = PPP_FSM_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 PPP_FSM_CLOSING:
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ /* fall through */
+ /* no break */
+ case PPP_FSM_CLOSED:
+ f->state = PPP_FSM_CLOSED;
+ if( f->callbacks->finished )
+ (*f->callbacks->finished)(f);
+ break;
+
+ case PPP_FSM_STOPPING:
+ case PPP_FSM_REQSENT:
+ case PPP_FSM_ACKRCVD:
+ case PPP_FSM_ACKSENT:
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ /* fall through */
+ /* no break */
+ case PPP_FSM_STOPPED:
+ f->state = PPP_FSM_STOPPED;
+ if( f->callbacks->finished )
+ (*f->callbacks->finished)(f);
+ break;
+
+ case PPP_FSM_OPENED:
+ terminate_layer(f, PPP_FSM_STOPPING);
+ break;
+
+ default:
+ FSMDEBUG(("%s: Protocol-reject event in state %d!",
+ PROTO_NAME(f), f->state));
+ /* no break */
+ }
+}
+
+
+/*
+ * fsm_sconfreq - Send a Configure-Request.
+ */
+static void fsm_sconfreq(fsm *f, int retransmit) {
+ ppp_pcb *pcb = f->pcb;
+ struct pbuf *p;
+ u_char *outp;
+ int cilen;
+
+ if( f->state != PPP_FSM_REQSENT && f->state != PPP_FSM_ACKRCVD && f->state != PPP_FSM_ACKSENT ){
+ /* Not currently negotiating - reset options */
+ if( f->callbacks->resetci )
+ (*f->callbacks->resetci)(f);
+ f->nakloops = 0;
+ f->rnakloops = 0;
+ }
+
+ if( !retransmit ){
+ /* New request - reset retransmission counter, use new ID */
+ f->retransmits = pcb->settings.fsm_max_conf_req_transmits;
+ f->reqid = ++f->id;
+ }
+
+ f->seen_ack = 0;
+
+ /*
+ * Make up the request packet
+ */
+ if( f->callbacks->cilen && f->callbacks->addci ){
+ cilen = (*f->callbacks->cilen)(f);
+ if( cilen > pcb->peer_mru - HEADERLEN )
+ cilen = pcb->peer_mru - HEADERLEN;
+ } else
+ cilen = 0;
+
+ p = pbuf_alloc(PBUF_RAW, (u16_t)(cilen + HEADERLEN + PPP_HDRLEN), PPP_CTRL_PBUF_TYPE);
+ if(NULL == p)
+ return;
+ if(p->tot_len != p->len) {
+ pbuf_free(p);
+ return;
+ }
+
+ /* send the request to our peer */
+ outp = (u_char*)p->payload;
+ MAKEHEADER(outp, f->protocol);
+ PUTCHAR(CONFREQ, outp);
+ PUTCHAR(f->reqid, outp);
+ PUTSHORT(cilen + HEADERLEN, outp);
+ if (cilen != 0) {
+ (*f->callbacks->addci)(f, outp, &cilen);
+ LWIP_ASSERT("cilen == p->len - HEADERLEN - PPP_HDRLEN", cilen == p->len - HEADERLEN - PPP_HDRLEN);
+ }
+
+ ppp_write(pcb, p);
+
+ /* start the retransmit timer */
+ --f->retransmits;
+ TIMEOUT(fsm_timeout, f, pcb->settings.fsm_timeout_time);
+}
+
+
+/*
+ * 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, const u_char *data, int datalen) {
+ ppp_pcb *pcb = f->pcb;
+ struct pbuf *p;
+ u_char *outp;
+ int outlen;
+
+ /* Adjust length to be smaller than MTU */
+ if (datalen > pcb->peer_mru - HEADERLEN)
+ datalen = pcb->peer_mru - HEADERLEN;
+ outlen = datalen + HEADERLEN;
+
+ p = pbuf_alloc(PBUF_RAW, (u16_t)(outlen + PPP_HDRLEN), PPP_CTRL_PBUF_TYPE);
+ if(NULL == p)
+ return;
+ if(p->tot_len != p->len) {
+ pbuf_free(p);
+ return;
+ }
+
+ outp = (u_char*)p->payload;
+ if (datalen) /* && data != outp + PPP_HDRLEN + HEADERLEN) -- was only for fsm_sconfreq() */
+ MEMCPY(outp + PPP_HDRLEN + HEADERLEN, data, datalen);
+ MAKEHEADER(outp, f->protocol);
+ PUTCHAR(code, outp);
+ PUTCHAR(id, outp);
+ PUTSHORT(outlen, outp);
+ ppp_write(pcb, p);
+}
+
+#endif /* PPP_SUPPORT */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/ipcp.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/ipcp.c
new file mode 100644
index 0000000..b7c766e
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/ipcp.c
@@ -0,0 +1,2418 @@
+/*
+ * ipcp.c - PPP IP Control Protocol.
+ *
+ * Copyright (c) 1984-2000 Carnegie Mellon University. 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 "Carnegie Mellon University" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission. For permission or any legal
+ * details, please contact
+ * Office of Technology Transfer
+ * Carnegie Mellon University
+ * 5000 Forbes Avenue
+ * Pittsburgh, PA 15213-3890
+ * (412) 268-4387, fax: (412) 268-7395
+ * tech-transfer@andrew.cmu.edu
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by Computing Services
+ * at Carnegie Mellon University (http://www.cmu.edu/computing/)."
+ *
+ * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
+ * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT && PPP_IPV4_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+/*
+ * @todo:
+ */
+
+#if 0 /* UNUSED */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <netdb.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#endif /* UNUSED */
+
+#include "netif/ppp/ppp_impl.h"
+
+#include "netif/ppp/fsm.h"
+#include "netif/ppp/ipcp.h"
+
+#if 0 /* UNUSED */
+/* global vars */
+u32_t netmask = 0; /* IP netmask to set on interface */
+#endif /* UNUSED */
+
+#if 0 /* UNUSED */
+bool disable_defaultip = 0; /* Don't use hostname for default IP adrs */
+#endif /* UNUSED */
+
+#if 0 /* moved to ppp_settings */
+bool noremoteip = 0; /* Let him have no IP address */
+#endif /* moved to ppp_setting */
+
+#if 0 /* UNUSED */
+/* Hook for a plugin to know when IP protocol has come up */
+void (*ip_up_hook) (void) = NULL;
+
+/* Hook for a plugin to know when IP protocol has come down */
+void (*ip_down_hook) (void) = NULL;
+
+/* Hook for a plugin to choose the remote IP address */
+void (*ip_choose_hook) (u32_t *) = NULL;
+#endif /* UNUSED */
+
+#if PPP_NOTIFY
+/* Notifiers for when IPCP goes up and down */
+struct notifier *ip_up_notifier = NULL;
+struct notifier *ip_down_notifier = NULL;
+#endif /* PPP_NOTIFY */
+
+/* local vars */
+#if 0 /* moved to ppp_pcb */
+static int default_route_set[NUM_PPP]; /* Have set up a default route */
+static int proxy_arp_set[NUM_PPP]; /* Have created proxy arp entry */
+static int ipcp_is_up; /* have called np_up() */
+static int ipcp_is_open; /* haven't called np_finished() */
+static bool ask_for_local; /* request our address from peer */
+#endif /* moved to ppp_pcb */
+#if 0 /* UNUSED */
+static char vj_value[8]; /* string form of vj option value */
+static char netmask_str[20]; /* string form of netmask value */
+#endif /* UNUSED */
+
+/*
+ * Callbacks for fsm code. (CI = Configuration Information)
+ */
+static void ipcp_resetci(fsm *f); /* Reset our CI */
+static int ipcp_cilen(fsm *f); /* Return length of our CI */
+static void ipcp_addci(fsm *f, u_char *ucp, int *lenp); /* Add our CI */
+static int ipcp_ackci(fsm *f, u_char *p, int len); /* Peer ack'd our CI */
+static int ipcp_nakci(fsm *f, u_char *p, int len, int treat_as_reject);/* Peer nak'd our CI */
+static int ipcp_rejci(fsm *f, u_char *p, int len); /* Peer rej'd our CI */
+static int ipcp_reqci(fsm *f, u_char *inp, int *len, int reject_if_disagree); /* Rcv CI */
+static void ipcp_up(fsm *f); /* We're UP */
+static void ipcp_down(fsm *f); /* We're DOWN */
+static void ipcp_finished(fsm *f); /* Don't need lower layer */
+
+static const 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 OPENED state */
+ ipcp_down, /* Called when fsm leaves 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 */
+};
+
+/*
+ * Command-line options.
+ */
+#if PPP_OPTIONS
+static int setvjslots (char **);
+static int setdnsaddr (char **);
+static int setwinsaddr (char **);
+static int setnetmask (char **);
+int setipaddr (char *, char **, int);
+
+static void printipaddr (option_t *, void (*)(void *, char *,...),void *);
+
+static option_t ipcp_option_list[] = {
+ { "noip", o_bool, &ipcp_protent.enabled_flag,
+ "Disable IP and IPCP" },
+ { "-ip", o_bool, &ipcp_protent.enabled_flag,
+ "Disable IP and IPCP", OPT_ALIAS },
+
+ { "novj", o_bool, &ipcp_wantoptions[0].neg_vj,
+ "Disable VJ compression", OPT_A2CLR, &ipcp_allowoptions[0].neg_vj },
+ { "-vj", o_bool, &ipcp_wantoptions[0].neg_vj,
+ "Disable VJ compression", OPT_ALIAS | OPT_A2CLR,
+ &ipcp_allowoptions[0].neg_vj },
+
+ { "novjccomp", o_bool, &ipcp_wantoptions[0].cflag,
+ "Disable VJ connection-ID compression", OPT_A2CLR,
+ &ipcp_allowoptions[0].cflag },
+ { "-vjccomp", o_bool, &ipcp_wantoptions[0].cflag,
+ "Disable VJ connection-ID compression", OPT_ALIAS | OPT_A2CLR,
+ &ipcp_allowoptions[0].cflag },
+
+ { "vj-max-slots", o_special, (void *)setvjslots,
+ "Set maximum VJ header slots",
+ OPT_PRIO | OPT_A2STRVAL | OPT_STATIC, vj_value },
+
+ { "ipcp-accept-local", o_bool, &ipcp_wantoptions[0].accept_local,
+ "Accept peer's address for us", 1 },
+ { "ipcp-accept-remote", o_bool, &ipcp_wantoptions[0].accept_remote,
+ "Accept peer's address for it", 1 },
+
+ { "ipparam", o_string, &ipparam,
+ "Set ip script parameter", OPT_PRIO },
+
+ { "noipdefault", o_bool, &disable_defaultip,
+ "Don't use name for default IP adrs", 1 },
+
+ { "ms-dns", 1, (void *)setdnsaddr,
+ "DNS address for the peer's use" },
+ { "ms-wins", 1, (void *)setwinsaddr,
+ "Nameserver for SMB over TCP/IP for peer" },
+
+ { "ipcp-restart", o_int, &ipcp_fsm[0].timeouttime,
+ "Set timeout for IPCP", OPT_PRIO },
+ { "ipcp-max-terminate", o_int, &ipcp_fsm[0].maxtermtransmits,
+ "Set max #xmits for term-reqs", OPT_PRIO },
+ { "ipcp-max-configure", o_int, &ipcp_fsm[0].maxconfreqtransmits,
+ "Set max #xmits for conf-reqs", OPT_PRIO },
+ { "ipcp-max-failure", o_int, &ipcp_fsm[0].maxnakloops,
+ "Set max #conf-naks for IPCP", OPT_PRIO },
+
+ { "defaultroute", o_bool, &ipcp_wantoptions[0].default_route,
+ "Add default route", OPT_ENABLE|1, &ipcp_allowoptions[0].default_route },
+ { "nodefaultroute", o_bool, &ipcp_allowoptions[0].default_route,
+ "disable defaultroute option", OPT_A2CLR,
+ &ipcp_wantoptions[0].default_route },
+ { "-defaultroute", o_bool, &ipcp_allowoptions[0].default_route,
+ "disable defaultroute option", OPT_ALIAS | OPT_A2CLR,
+ &ipcp_wantoptions[0].default_route },
+
+ { "replacedefaultroute", o_bool,
+ &ipcp_wantoptions[0].replace_default_route,
+ "Replace default route", 1
+ },
+ { "noreplacedefaultroute", o_bool,
+ &ipcp_allowoptions[0].replace_default_route,
+ "Never replace default route", OPT_A2COPY,
+ &ipcp_wantoptions[0].replace_default_route },
+ { "proxyarp", o_bool, &ipcp_wantoptions[0].proxy_arp,
+ "Add proxy ARP entry", OPT_ENABLE|1, &ipcp_allowoptions[0].proxy_arp },
+ { "noproxyarp", o_bool, &ipcp_allowoptions[0].proxy_arp,
+ "disable proxyarp option", OPT_A2CLR,
+ &ipcp_wantoptions[0].proxy_arp },
+ { "-proxyarp", o_bool, &ipcp_allowoptions[0].proxy_arp,
+ "disable proxyarp option", OPT_ALIAS | OPT_A2CLR,
+ &ipcp_wantoptions[0].proxy_arp },
+
+ { "usepeerdns", o_bool, &usepeerdns,
+ "Ask peer for DNS address(es)", 1 },
+
+ { "netmask", o_special, (void *)setnetmask,
+ "set netmask", OPT_PRIO | OPT_A2STRVAL | OPT_STATIC, netmask_str },
+
+ { "ipcp-no-addresses", o_bool, &ipcp_wantoptions[0].old_addrs,
+ "Disable old-style IP-Addresses usage", OPT_A2CLR,
+ &ipcp_allowoptions[0].old_addrs },
+ { "ipcp-no-address", o_bool, &ipcp_wantoptions[0].neg_addr,
+ "Disable IP-Address usage", OPT_A2CLR,
+ &ipcp_allowoptions[0].neg_addr },
+
+ { "noremoteip", o_bool, &noremoteip,
+ "Allow peer to have no IP address", 1 },
+
+ { "nosendip", o_bool, &ipcp_wantoptions[0].neg_addr,
+ "Don't send our IP address to peer", OPT_A2CLR,
+ &ipcp_wantoptions[0].old_addrs},
+
+ { "IP addresses", o_wild, (void *) &setipaddr,
+ "set local and remote IP addresses",
+ OPT_NOARG | OPT_A2PRINTER, (void *) &printipaddr },
+
+ { NULL }
+};
+#endif /* PPP_OPTIONS */
+
+/*
+ * Protocol entry points from main code.
+ */
+static void ipcp_init(ppp_pcb *pcb);
+static void ipcp_open(ppp_pcb *pcb);
+static void ipcp_close(ppp_pcb *pcb, const char *reason);
+static void ipcp_lowerup(ppp_pcb *pcb);
+static void ipcp_lowerdown(ppp_pcb *pcb);
+static void ipcp_input(ppp_pcb *pcb, u_char *p, int len);
+static void ipcp_protrej(ppp_pcb *pcb);
+#if PRINTPKT_SUPPORT
+static int ipcp_printpkt(const u_char *p, int plen,
+ void (*printer) (void *, const char *, ...), void *arg);
+#endif /* PRINTPKT_SUPPORT */
+#if PPP_OPTIONS
+static void ip_check_options (void);
+#endif /* PPP_OPTIONS */
+#if DEMAND_SUPPORT
+static int ip_demand_conf (int);
+static int ip_active_pkt (u_char *, int);
+#endif /* DEMAND_SUPPORT */
+#if 0 /* UNUSED */
+static void create_resolv (u32_t, u32_t);
+#endif /* UNUSED */
+
+const struct protent ipcp_protent = {
+ PPP_IPCP,
+ ipcp_init,
+ ipcp_input,
+ ipcp_protrej,
+ ipcp_lowerup,
+ ipcp_lowerdown,
+ ipcp_open,
+ ipcp_close,
+#if PRINTPKT_SUPPORT
+ ipcp_printpkt,
+#endif /* PRINTPKT_SUPPORT */
+#if PPP_DATAINPUT
+ NULL,
+#endif /* PPP_DATAINPUT */
+#if PRINTPKT_SUPPORT
+ "IPCP",
+ "IP",
+#endif /* PRINTPKT_SUPPORT */
+#if PPP_OPTIONS
+ ipcp_option_list,
+ ip_check_options,
+#endif /* PPP_OPTIONS */
+#if DEMAND_SUPPORT
+ ip_demand_conf,
+ ip_active_pkt
+#endif /* DEMAND_SUPPORT */
+};
+
+static void ipcp_clear_addrs(ppp_pcb *pcb, u32_t ouraddr, u32_t hisaddr, u8_t replacedefaultroute);
+
+/*
+ * 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")
+
+#if 0 /* UNUSED, already defined by lwIP */
+/*
+ * Make a string representation of a network IP address.
+ */
+char *
+ip_ntoa(ipaddr)
+u32_t ipaddr;
+{
+ static char b[64];
+
+ slprintf(b, sizeof(b), "%I", ipaddr);
+ return b;
+}
+#endif /* UNUSED, already defined by lwIP */
+
+/*
+ * Option parsing.
+ */
+#if PPP_OPTIONS
+/*
+ * setvjslots - set maximum number of connection slots for VJ compression
+ */
+static int
+setvjslots(argv)
+ char **argv;
+{
+ int value;
+
+ if (!int_option(*argv, &value))
+ return 0;
+
+ if (value < 2 || value > 16) {
+ option_error("vj-max-slots value must be between 2 and 16");
+ return 0;
+ }
+ ipcp_wantoptions [0].maxslotindex =
+ ipcp_allowoptions[0].maxslotindex = value - 1;
+ slprintf(vj_value, sizeof(vj_value), "%d", value);
+ return 1;
+}
+
+/*
+ * setdnsaddr - set the dns address(es)
+ */
+static int
+setdnsaddr(argv)
+ char **argv;
+{
+ u32_t dns;
+ struct hostent *hp;
+
+ dns = inet_addr(*argv);
+ if (dns == (u32_t) -1) {
+ if ((hp = gethostbyname(*argv)) == NULL) {
+ option_error("invalid address parameter '%s' for ms-dns option",
+ *argv);
+ return 0;
+ }
+ dns = *(u32_t *)hp->h_addr;
+ }
+
+ /* We take the last 2 values given, the 2nd-last as the primary
+ and the last as the secondary. If only one is given it
+ becomes both primary and secondary. */
+ if (ipcp_allowoptions[0].dnsaddr[1] == 0)
+ ipcp_allowoptions[0].dnsaddr[0] = dns;
+ else
+ ipcp_allowoptions[0].dnsaddr[0] = ipcp_allowoptions[0].dnsaddr[1];
+
+ /* always set the secondary address value. */
+ ipcp_allowoptions[0].dnsaddr[1] = dns;
+
+ return (1);
+}
+
+/*
+ * setwinsaddr - set the wins address(es)
+ * This is primrarly used with the Samba package under UNIX or for pointing
+ * the caller to the existing WINS server on a Windows NT platform.
+ */
+static int
+setwinsaddr(argv)
+ char **argv;
+{
+ u32_t wins;
+ struct hostent *hp;
+
+ wins = inet_addr(*argv);
+ if (wins == (u32_t) -1) {
+ if ((hp = gethostbyname(*argv)) == NULL) {
+ option_error("invalid address parameter '%s' for ms-wins option",
+ *argv);
+ return 0;
+ }
+ wins = *(u32_t *)hp->h_addr;
+ }
+
+ /* We take the last 2 values given, the 2nd-last as the primary
+ and the last as the secondary. If only one is given it
+ becomes both primary and secondary. */
+ if (ipcp_allowoptions[0].winsaddr[1] == 0)
+ ipcp_allowoptions[0].winsaddr[0] = wins;
+ else
+ ipcp_allowoptions[0].winsaddr[0] = ipcp_allowoptions[0].winsaddr[1];
+
+ /* always set the secondary address value. */
+ ipcp_allowoptions[0].winsaddr[1] = wins;
+
+ return (1);
+}
+
+/*
+ * setipaddr - Set the IP address
+ * If doit is 0, the call is to check whether this option is
+ * potentially an IP address specification.
+ * Not static so that plugins can call it to set the addresses
+ */
+int
+setipaddr(arg, argv, doit)
+ char *arg;
+ char **argv;
+ int doit;
+{
+ struct hostent *hp;
+ char *colon;
+ u32_t local, remote;
+ ipcp_options *wo = &ipcp_wantoptions[0];
+ static int prio_local = 0, prio_remote = 0;
+
+ /*
+ * IP address pair separated by ":".
+ */
+ if ((colon = strchr(arg, ':')) == NULL)
+ return 0;
+ if (!doit)
+ return 1;
+
+ /*
+ * If colon first character, then no local addr.
+ */
+ if (colon != arg && option_priority >= prio_local) {
+ *colon = '\0';
+ if ((local = inet_addr(arg)) == (u32_t) -1) {
+ if ((hp = gethostbyname(arg)) == NULL) {
+ option_error("unknown host: %s", arg);
+ return 0;
+ }
+ local = *(u32_t *)hp->h_addr;
+ }
+ if (bad_ip_adrs(local)) {
+ option_error("bad local IP address %s", ip_ntoa(local));
+ return 0;
+ }
+ if (local != 0)
+ wo->ouraddr = local;
+ *colon = ':';
+ prio_local = option_priority;
+ }
+
+ /*
+ * If colon last character, then no remote addr.
+ */
+ if (*++colon != '\0' && option_priority >= prio_remote) {
+ if ((remote = inet_addr(colon)) == (u32_t) -1) {
+ if ((hp = gethostbyname(colon)) == NULL) {
+ option_error("unknown host: %s", colon);
+ return 0;
+ }
+ remote = *(u32_t *)hp->h_addr;
+ if (remote_name[0] == 0)
+ strlcpy(remote_name, colon, sizeof(remote_name));
+ }
+ if (bad_ip_adrs(remote)) {
+ option_error("bad remote IP address %s", ip_ntoa(remote));
+ return 0;
+ }
+ if (remote != 0)
+ wo->hisaddr = remote;
+ prio_remote = option_priority;
+ }
+
+ return 1;
+}
+
+static void
+printipaddr(opt, printer, arg)
+ option_t *opt;
+ void (*printer) (void *, char *, ...);
+ void *arg;
+{
+ ipcp_options *wo = &ipcp_wantoptions[0];
+
+ if (wo->ouraddr != 0)
+ printer(arg, "%I", wo->ouraddr);
+ printer(arg, ":");
+ if (wo->hisaddr != 0)
+ printer(arg, "%I", wo->hisaddr);
+}
+
+/*
+ * setnetmask - set the netmask to be used on the interface.
+ */
+static int
+setnetmask(argv)
+ char **argv;
+{
+ u32_t mask;
+ int n;
+ char *p;
+
+ /*
+ * Unfortunately, if we use inet_addr, we can't tell whether
+ * a result of all 1s is an error or a valid 255.255.255.255.
+ */
+ p = *argv;
+ n = parse_dotted_ip(p, &mask);
+
+ mask = lwip_htonl(mask);
+
+ if (n == 0 || p[n] != 0 || (netmask & ~mask) != 0) {
+ option_error("invalid netmask value '%s'", *argv);
+ return 0;
+ }
+
+ netmask = mask;
+ slprintf(netmask_str, sizeof(netmask_str), "%I", mask);
+
+ return (1);
+}
+
+int
+parse_dotted_ip(p, vp)
+ char *p;
+ u32_t *vp;
+{
+ int n;
+ u32_t v, b;
+ char *endp, *p0 = p;
+
+ v = 0;
+ for (n = 3;; --n) {
+ b = strtoul(p, &endp, 0);
+ if (endp == p)
+ return 0;
+ if (b > 255) {
+ if (n < 3)
+ return 0;
+ /* accept e.g. 0xffffff00 */
+ *vp = b;
+ return endp - p0;
+ }
+ v |= b << (n * 8);
+ p = endp;
+ if (n == 0)
+ break;
+ if (*p != '.')
+ return 0;
+ ++p;
+ }
+ *vp = v;
+ return p - p0;
+}
+#endif /* PPP_OPTIONS */
+
+/*
+ * ipcp_init - Initialize IPCP.
+ */
+static void ipcp_init(ppp_pcb *pcb) {
+ fsm *f = &pcb->ipcp_fsm;
+
+ ipcp_options *wo = &pcb->ipcp_wantoptions;
+ ipcp_options *ao = &pcb->ipcp_allowoptions;
+
+ f->pcb = pcb;
+ f->protocol = PPP_IPCP;
+ f->callbacks = &ipcp_callbacks;
+ fsm_init(f);
+
+ /*
+ * Some 3G modems use repeated IPCP NAKs as a way of stalling
+ * until they can contact a server on the network, so we increase
+ * the default number of NAKs we accept before we start treating
+ * them as rejects.
+ */
+ f->maxnakloops = 100;
+
+#if 0 /* Not necessary, everything is cleared in ppp_new() */
+ memset(wo, 0, sizeof(*wo));
+ memset(ao, 0, sizeof(*ao));
+#endif /* 0 */
+
+ wo->neg_addr = wo->old_addrs = 1;
+#if VJ_SUPPORT
+ wo->neg_vj = 1;
+ wo->vj_protocol = IPCP_VJ_COMP;
+ wo->maxslotindex = MAX_STATES - 1; /* really max index */
+ wo->cflag = 1;
+#endif /* VJ_SUPPORT */
+
+#if 0 /* UNUSED */
+ /* wanting default route by default */
+ wo->default_route = 1;
+#endif /* UNUSED */
+
+ ao->neg_addr = ao->old_addrs = 1;
+#if VJ_SUPPORT
+ /* max slots and slot-id compression are currently hardwired in */
+ /* ppp_if.c to 16 and 1, this needs to be changed (among other */
+ /* things) gmc */
+
+ ao->neg_vj = 1;
+ ao->maxslotindex = MAX_STATES - 1;
+ ao->cflag = 1;
+#endif /* #if VJ_SUPPORT */
+
+#if 0 /* UNUSED */
+ /*
+ * XXX These control whether the user may use the proxyarp
+ * and defaultroute options.
+ */
+ ao->proxy_arp = 1;
+ ao->default_route = 1;
+#endif /* UNUSED */
+}
+
+
+/*
+ * ipcp_open - IPCP is allowed to come up.
+ */
+static void ipcp_open(ppp_pcb *pcb) {
+ fsm *f = &pcb->ipcp_fsm;
+ fsm_open(f);
+ pcb->ipcp_is_open = 1;
+}
+
+
+/*
+ * ipcp_close - Take IPCP down.
+ */
+static void ipcp_close(ppp_pcb *pcb, const char *reason) {
+ fsm *f = &pcb->ipcp_fsm;
+ fsm_close(f, reason);
+}
+
+
+/*
+ * ipcp_lowerup - The lower layer is up.
+ */
+static void ipcp_lowerup(ppp_pcb *pcb) {
+ fsm *f = &pcb->ipcp_fsm;
+ fsm_lowerup(f);
+}
+
+
+/*
+ * ipcp_lowerdown - The lower layer is down.
+ */
+static void ipcp_lowerdown(ppp_pcb *pcb) {
+ fsm *f = &pcb->ipcp_fsm;
+ fsm_lowerdown(f);
+}
+
+
+/*
+ * ipcp_input - Input IPCP packet.
+ */
+static void ipcp_input(ppp_pcb *pcb, u_char *p, int len) {
+ fsm *f = &pcb->ipcp_fsm;
+ fsm_input(f, 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(ppp_pcb *pcb) {
+ fsm *f = &pcb->ipcp_fsm;
+ fsm_lowerdown(f);
+}
+
+
+/*
+ * ipcp_resetci - Reset our CI.
+ * Called by fsm_sconfreq, Send Configure Request.
+ */
+static void ipcp_resetci(fsm *f) {
+ ppp_pcb *pcb = f->pcb;
+ ipcp_options *wo = &pcb->ipcp_wantoptions;
+ ipcp_options *go = &pcb->ipcp_gotoptions;
+ ipcp_options *ao = &pcb->ipcp_allowoptions;
+
+ wo->req_addr = (wo->neg_addr || wo->old_addrs) &&
+ (ao->neg_addr || ao->old_addrs);
+ if (wo->ouraddr == 0)
+ wo->accept_local = 1;
+ if (wo->hisaddr == 0)
+ wo->accept_remote = 1;
+#if LWIP_DNS
+ wo->req_dns1 = wo->req_dns2 = pcb->settings.usepeerdns; /* Request DNS addresses from the peer */
+#endif /* LWIP_DNS */
+ *go = *wo;
+ if (!pcb->ask_for_local)
+ go->ouraddr = 0;
+#if 0 /* UNUSED */
+ if (ip_choose_hook) {
+ ip_choose_hook(&wo->hisaddr);
+ if (wo->hisaddr) {
+ wo->accept_remote = 0;
+ }
+ }
+#endif /* UNUSED */
+ BZERO(&pcb->ipcp_hisoptions, sizeof(ipcp_options));
+}
+
+
+/*
+ * ipcp_cilen - Return length of our CI.
+ * Called by fsm_sconfreq, Send Configure Request.
+ */
+static int ipcp_cilen(fsm *f) {
+ ppp_pcb *pcb = f->pcb;
+ ipcp_options *go = &pcb->ipcp_gotoptions;
+#if VJ_SUPPORT
+ ipcp_options *wo = &pcb->ipcp_wantoptions;
+#endif /* VJ_SUPPORT */
+ ipcp_options *ho = &pcb->ipcp_hisoptions;
+
+#define LENCIADDRS(neg) (neg ? CILEN_ADDRS : 0)
+#if VJ_SUPPORT
+#define LENCIVJ(neg, old) (neg ? (old? CILEN_COMPRESS : CILEN_VJ) : 0)
+#endif /* VJ_SUPPORT */
+#define LENCIADDR(neg) (neg ? CILEN_ADDR : 0)
+#if LWIP_DNS
+#define LENCIDNS(neg) LENCIADDR(neg)
+#endif /* LWIP_DNS */
+#if 0 /* UNUSED - WINS */
+#define LENCIWINS(neg) LENCIADDR(neg)
+#endif /* UNUSED - WINS */
+
+ /*
+ * First see if we want to change our options to the old
+ * forms because we have received old forms from the peer.
+ */
+ if (go->neg_addr && go->old_addrs && !ho->neg_addr && ho->old_addrs)
+ go->neg_addr = 0;
+
+#if VJ_SUPPORT
+ if (wo->neg_vj && !go->neg_vj && !go->old_vj) {
+ /* try an older style of VJ negotiation */
+ /* 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;
+ }
+ }
+#endif /* VJ_SUPPORT */
+
+ return (LENCIADDRS(!go->neg_addr && go->old_addrs) +
+#if VJ_SUPPORT
+ LENCIVJ(go->neg_vj, go->old_vj) +
+#endif /* VJ_SUPPORT */
+ LENCIADDR(go->neg_addr) +
+#if LWIP_DNS
+ LENCIDNS(go->req_dns1) +
+ LENCIDNS(go->req_dns2) +
+#endif /* LWIP_DNS */
+#if 0 /* UNUSED - WINS */
+ LENCIWINS(go->winsaddr[0]) +
+ LENCIWINS(go->winsaddr[1]) +
+#endif /* UNUSED - WINS */
+ 0);
+}
+
+
+/*
+ * ipcp_addci - Add our desired CIs to a packet.
+ * Called by fsm_sconfreq, Send Configure Request.
+ */
+static void ipcp_addci(fsm *f, u_char *ucp, int *lenp) {
+ ppp_pcb *pcb = f->pcb;
+ ipcp_options *go = &pcb->ipcp_gotoptions;
+ int len = *lenp;
+
+#define ADDCIADDRS(opt, neg, val1, val2) \
+ if (neg) { \
+ if (len >= CILEN_ADDRS) { \
+ u32_t l; \
+ PUTCHAR(opt, ucp); \
+ PUTCHAR(CILEN_ADDRS, ucp); \
+ l = lwip_ntohl(val1); \
+ PUTLONG(l, ucp); \
+ l = lwip_ntohl(val2); \
+ PUTLONG(l, ucp); \
+ len -= CILEN_ADDRS; \
+ } else \
+ go->old_addrs = 0; \
+ }
+
+#if VJ_SUPPORT
+#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; \
+ }
+#endif /* VJ_SUPPORT */
+
+#define ADDCIADDR(opt, neg, val) \
+ if (neg) { \
+ if (len >= CILEN_ADDR) { \
+ u32_t l; \
+ PUTCHAR(opt, ucp); \
+ PUTCHAR(CILEN_ADDR, ucp); \
+ l = lwip_ntohl(val); \
+ PUTLONG(l, ucp); \
+ len -= CILEN_ADDR; \
+ } else \
+ neg = 0; \
+ }
+
+#if LWIP_DNS
+#define ADDCIDNS(opt, neg, addr) \
+ if (neg) { \
+ if (len >= CILEN_ADDR) { \
+ u32_t l; \
+ PUTCHAR(opt, ucp); \
+ PUTCHAR(CILEN_ADDR, ucp); \
+ l = lwip_ntohl(addr); \
+ PUTLONG(l, ucp); \
+ len -= CILEN_ADDR; \
+ } else \
+ neg = 0; \
+ }
+#endif /* LWIP_DNS */
+
+#if 0 /* UNUSED - WINS */
+#define ADDCIWINS(opt, addr) \
+ if (addr) { \
+ if (len >= CILEN_ADDR) { \
+ u32_t l; \
+ PUTCHAR(opt, ucp); \
+ PUTCHAR(CILEN_ADDR, ucp); \
+ l = lwip_ntohl(addr); \
+ PUTLONG(l, ucp); \
+ len -= CILEN_ADDR; \
+ } else \
+ addr = 0; \
+ }
+#endif /* UNUSED - WINS */
+
+ ADDCIADDRS(CI_ADDRS, !go->neg_addr && go->old_addrs, go->ouraddr,
+ go->hisaddr);
+
+#if VJ_SUPPORT
+ ADDCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol, go->old_vj,
+ go->maxslotindex, go->cflag);
+#endif /* VJ_SUPPORT */
+
+ ADDCIADDR(CI_ADDR, go->neg_addr, go->ouraddr);
+
+#if LWIP_DNS
+ ADDCIDNS(CI_MS_DNS1, go->req_dns1, go->dnsaddr[0]);
+
+ ADDCIDNS(CI_MS_DNS2, go->req_dns2, go->dnsaddr[1]);
+#endif /* LWIP_DNS */
+
+#if 0 /* UNUSED - WINS */
+ ADDCIWINS(CI_MS_WINS1, go->winsaddr[0]);
+
+ ADDCIWINS(CI_MS_WINS2, go->winsaddr[1]);
+#endif /* UNUSED - WINS */
+
+ *lenp -= len;
+}
+
+
+/*
+ * ipcp_ackci - Ack our CIs.
+ * Called by fsm_rconfack, Receive Configure ACK.
+ *
+ * Returns:
+ * 0 - Ack was bad.
+ * 1 - Ack was good.
+ */
+static int ipcp_ackci(fsm *f, u_char *p, int len) {
+ ppp_pcb *pcb = f->pcb;
+ ipcp_options *go = &pcb->ipcp_gotoptions;
+ u_short cilen, citype;
+ u32_t cilong;
+#if VJ_SUPPORT
+ u_short cishort;
+ u_char cimaxslotindex, cicflag;
+#endif /* VJ_SUPPORT */
+
+ /*
+ * 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 ACKCIADDRS(opt, neg, val1, val2) \
+ if (neg) { \
+ u32_t l; \
+ if ((len -= CILEN_ADDRS) < 0) \
+ goto bad; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != CILEN_ADDRS || \
+ citype != opt) \
+ goto bad; \
+ GETLONG(l, p); \
+ cilong = lwip_htonl(l); \
+ if (val1 != cilong) \
+ goto bad; \
+ GETLONG(l, p); \
+ cilong = lwip_htonl(l); \
+ if (val2 != cilong) \
+ goto bad; \
+ }
+
+#if VJ_SUPPORT
+#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; \
+ } \
+ }
+#endif /* VJ_SUPPORT */
+
+#define ACKCIADDR(opt, neg, val) \
+ 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 = lwip_htonl(l); \
+ if (val != cilong) \
+ goto bad; \
+ }
+
+#if LWIP_DNS
+#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 = lwip_htonl(l); \
+ if (addr != cilong) \
+ goto bad; \
+ }
+#endif /* LWIP_DNS */
+
+#if 0 /* UNUSED - WINS */
+#define ACKCIWINS(opt, addr) \
+ if (addr) { \
+ 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 = lwip_htonl(l); \
+ if (addr != cilong) \
+ goto bad; \
+ }
+#endif /* UNUSED - WINS */
+
+ ACKCIADDRS(CI_ADDRS, !go->neg_addr && go->old_addrs, go->ouraddr,
+ go->hisaddr);
+
+#if VJ_SUPPORT
+ ACKCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol, go->old_vj,
+ go->maxslotindex, go->cflag);
+#endif /* VJ_SUPPORT */
+
+ ACKCIADDR(CI_ADDR, go->neg_addr, go->ouraddr);
+
+#if LWIP_DNS
+ ACKCIDNS(CI_MS_DNS1, go->req_dns1, go->dnsaddr[0]);
+
+ ACKCIDNS(CI_MS_DNS2, go->req_dns2, go->dnsaddr[1]);
+#endif /* LWIP_DNS */
+
+#if 0 /* UNUSED - WINS */
+ ACKCIWINS(CI_MS_WINS1, go->winsaddr[0]);
+
+ ACKCIWINS(CI_MS_WINS2, go->winsaddr[1]);
+#endif /* UNUSED - WINS */
+
+ /*
+ * If there are any remaining CIs, then this packet is bad.
+ */
+ if (len != 0)
+ goto bad;
+ return (1);
+
+bad:
+ IPCPDEBUG(("ipcp_ackci: received bad Ack!"));
+ 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 OPENED state.
+ * Calback from fsm_rconfnakrej - Receive Configure-Nak or Configure-Reject.
+ *
+ * Returns:
+ * 0 - Nak was bad.
+ * 1 - Nak was good.
+ */
+static int ipcp_nakci(fsm *f, u_char *p, int len, int treat_as_reject) {
+ ppp_pcb *pcb = f->pcb;
+ ipcp_options *go = &pcb->ipcp_gotoptions;
+ u_char citype, cilen, *next;
+#if VJ_SUPPORT
+ u_char cimaxslotindex, cicflag;
+ u_short cishort;
+#endif /* VJ_SUPPORT */
+ u32_t ciaddr1, ciaddr2, l;
+#if LWIP_DNS
+ u32_t cidnsaddr;
+#endif /* LWIP_DNS */
+ 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 NAKCIADDRS(opt, neg, code) \
+ if ((neg) && \
+ (cilen = p[1]) == CILEN_ADDRS && \
+ len >= cilen && \
+ p[0] == opt) { \
+ len -= cilen; \
+ INCPTR(2, p); \
+ GETLONG(l, p); \
+ ciaddr1 = lwip_htonl(l); \
+ GETLONG(l, p); \
+ ciaddr2 = lwip_htonl(l); \
+ no.old_addrs = 1; \
+ code \
+ }
+
+#if VJ_SUPPORT
+#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 \
+ }
+#endif /* VJ_SUPPORT */
+
+#define NAKCIADDR(opt, neg, code) \
+ if (go->neg && \
+ (cilen = p[1]) == CILEN_ADDR && \
+ len >= cilen && \
+ p[0] == opt) { \
+ len -= cilen; \
+ INCPTR(2, p); \
+ GETLONG(l, p); \
+ ciaddr1 = lwip_htonl(l); \
+ no.neg = 1; \
+ code \
+ }
+
+#if LWIP_DNS
+#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 = lwip_htonl(l); \
+ no.neg = 1; \
+ code \
+ }
+#endif /* LWIP_DNS */
+
+ /*
+ * Accept the peer's idea of {our,his} address, if different
+ * from our idea, only if the accept_{local,remote} flag is set.
+ */
+ NAKCIADDRS(CI_ADDRS, !go->neg_addr && go->old_addrs,
+ if (treat_as_reject) {
+ try_.old_addrs = 0;
+ } else {
+ if (go->accept_local && ciaddr1) {
+ /* take his idea of our address */
+ try_.ouraddr = ciaddr1;
+ }
+ if (go->accept_remote && ciaddr2) {
+ /* take his idea of his address */
+ try_.hisaddr = ciaddr2;
+ }
+ }
+ );
+
+#if VJ_SUPPORT
+ /*
+ * 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 (treat_as_reject) {
+ try_.neg_vj = 0;
+ } else 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;
+ }
+ }
+ );
+#endif /* VJ_SUPPORT */
+
+ NAKCIADDR(CI_ADDR, neg_addr,
+ if (treat_as_reject) {
+ try_.neg_addr = 0;
+ try_.old_addrs = 0;
+ } else if (go->accept_local && ciaddr1) {
+ /* take his idea of our address */
+ try_.ouraddr = ciaddr1;
+ }
+ );
+
+#if LWIP_DNS
+ NAKCIDNS(CI_MS_DNS1, req_dns1,
+ if (treat_as_reject) {
+ try_.req_dns1 = 0;
+ } else {
+ try_.dnsaddr[0] = cidnsaddr;
+ }
+ );
+
+ NAKCIDNS(CI_MS_DNS2, req_dns2,
+ if (treat_as_reject) {
+ try_.req_dns2 = 0;
+ } else {
+ try_.dnsaddr[1] = cidnsaddr;
+ }
+ );
+#endif /* #if LWIP_DNS */
+
+ /*
+ * 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.
+ * If they want us to ask for ms-dns, we do that, since some
+ * peers get huffy if we don't.
+ */
+ 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) {
+#if VJ_SUPPORT
+ case CI_COMPRESSTYPE:
+ if (go->neg_vj || no.neg_vj ||
+ (cilen != CILEN_VJ && cilen != CILEN_COMPRESS))
+ goto bad;
+ no.neg_vj = 1;
+ break;
+#endif /* VJ_SUPPORT */
+ case CI_ADDRS:
+ if ((!go->neg_addr && go->old_addrs) || no.old_addrs
+ || cilen != CILEN_ADDRS)
+ goto bad;
+ try_.neg_addr = 0;
+ GETLONG(l, p);
+ ciaddr1 = lwip_htonl(l);
+ if (ciaddr1 && go->accept_local)
+ try_.ouraddr = ciaddr1;
+ GETLONG(l, p);
+ ciaddr2 = lwip_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 = lwip_htonl(l);
+ if (ciaddr1 && go->accept_local)
+ try_.ouraddr = ciaddr1;
+ if (try_.ouraddr != 0)
+ try_.neg_addr = 1;
+ no.neg_addr = 1;
+ break;
+#if LWIP_DNS
+ case CI_MS_DNS1:
+ if (go->req_dns1 || no.req_dns1 || cilen != CILEN_ADDR)
+ goto bad;
+ GETLONG(l, p);
+ try_.dnsaddr[0] = lwip_htonl(l);
+ try_.req_dns1 = 1;
+ no.req_dns1 = 1;
+ break;
+ case CI_MS_DNS2:
+ if (go->req_dns2 || no.req_dns2 || cilen != CILEN_ADDR)
+ goto bad;
+ GETLONG(l, p);
+ try_.dnsaddr[1] = lwip_htonl(l);
+ try_.req_dns2 = 1;
+ no.req_dns2 = 1;
+ break;
+#endif /* LWIP_DNS */
+#if 0 /* UNUSED - WINS */
+ case CI_MS_WINS1:
+ case CI_MS_WINS2:
+ if (cilen != CILEN_ADDR)
+ goto bad;
+ GETLONG(l, p);
+ ciaddr1 = lwip_htonl(l);
+ if (ciaddr1)
+ try_.winsaddr[citype == CI_MS_WINS2] = ciaddr1;
+ break;
+#endif /* UNUSED - WINS */
+ default:
+ break;
+ }
+ p = next;
+ }
+
+ /*
+ * OK, the Nak is good. Now we can update state.
+ * If there are any remaining options, we ignore them.
+ */
+ if (f->state != PPP_FSM_OPENED)
+ *go = try_;
+
+ return 1;
+
+bad:
+ IPCPDEBUG(("ipcp_nakci: received bad Nak!"));
+ return 0;
+}
+
+
+/*
+ * ipcp_rejci - Reject some of our CIs.
+ * Callback from fsm_rconfnakrej.
+ */
+static int ipcp_rejci(fsm *f, u_char *p, int len) {
+ ppp_pcb *pcb = f->pcb;
+ ipcp_options *go = &pcb->ipcp_gotoptions;
+ u_char cilen;
+#if VJ_SUPPORT
+ u_char cimaxslotindex, ciflag;
+ u_short cishort;
+#endif /* VJ_SUPPORT */
+ 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 REJCIADDRS(opt, neg, val1, val2) \
+ if ((neg) && \
+ (cilen = p[1]) == CILEN_ADDRS && \
+ len >= cilen && \
+ p[0] == opt) { \
+ u32_t l; \
+ len -= cilen; \
+ INCPTR(2, p); \
+ GETLONG(l, p); \
+ cilong = lwip_htonl(l); \
+ /* Check rejected value. */ \
+ if (cilong != val1) \
+ goto bad; \
+ GETLONG(l, p); \
+ cilong = lwip_htonl(l); \
+ /* Check rejected value. */ \
+ if (cilong != val2) \
+ goto bad; \
+ try_.old_addrs = 0; \
+ }
+
+#if VJ_SUPPORT
+#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; \
+ }
+#endif /* VJ_SUPPORT */
+
+#define REJCIADDR(opt, neg, val) \
+ 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 = lwip_htonl(l); \
+ /* Check rejected value. */ \
+ if (cilong != val) \
+ goto bad; \
+ try_.neg = 0; \
+ }
+
+#if LWIP_DNS
+#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 = lwip_htonl(l); \
+ /* Check rejected value. */ \
+ if (cilong != dnsaddr) \
+ goto bad; \
+ try_.neg = 0; \
+ }
+#endif /* LWIP_DNS */
+
+#if 0 /* UNUSED - WINS */
+#define REJCIWINS(opt, addr) \
+ if (addr && \
+ ((cilen = p[1]) == CILEN_ADDR) && \
+ len >= cilen && \
+ p[0] == opt) { \
+ u32_t l; \
+ len -= cilen; \
+ INCPTR(2, p); \
+ GETLONG(l, p); \
+ cilong = lwip_htonl(l); \
+ /* Check rejected value. */ \
+ if (cilong != addr) \
+ goto bad; \
+ try_.winsaddr[opt == CI_MS_WINS2] = 0; \
+ }
+#endif /* UNUSED - WINS */
+
+ REJCIADDRS(CI_ADDRS, !go->neg_addr && go->old_addrs,
+ go->ouraddr, go->hisaddr);
+
+#if VJ_SUPPORT
+ REJCIVJ(CI_COMPRESSTYPE, neg_vj, go->vj_protocol, go->old_vj,
+ go->maxslotindex, go->cflag);
+#endif /* VJ_SUPPORT */
+
+ REJCIADDR(CI_ADDR, neg_addr, go->ouraddr);
+
+#if LWIP_DNS
+ REJCIDNS(CI_MS_DNS1, req_dns1, go->dnsaddr[0]);
+
+ REJCIDNS(CI_MS_DNS2, req_dns2, go->dnsaddr[1]);
+#endif /* LWIP_DNS */
+
+#if 0 /* UNUSED - WINS */
+ REJCIWINS(CI_MS_WINS1, go->winsaddr[0]);
+
+ REJCIWINS(CI_MS_WINS2, go->winsaddr[1]);
+#endif /* UNUSED - WINS */
+
+ /*
+ * If there are any remaining CIs, then this packet is bad.
+ */
+ if (len != 0)
+ goto bad;
+ /*
+ * Now we can update state.
+ */
+ if (f->state != PPP_FSM_OPENED)
+ *go = try_;
+ return 1;
+
+bad:
+ IPCPDEBUG(("ipcp_rejci: received bad Reject!"));
+ return 0;
+}
+
+
+/*
+ * ipcp_reqci - Check the peer's requested CIs and send appropriate response.
+ * Callback from fsm_rconfreq, Receive Configure Request
+ *
+ * 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.
+ *
+ * inp = Requested CIs
+ * len = Length of requested CIs
+ */
+static int ipcp_reqci(fsm *f, u_char *inp, int *len, int reject_if_disagree) {
+ ppp_pcb *pcb = f->pcb;
+ ipcp_options *wo = &pcb->ipcp_wantoptions;
+ ipcp_options *ho = &pcb->ipcp_hisoptions;
+ ipcp_options *ao = &pcb->ipcp_allowoptions;
+ u_char *cip, *next; /* Pointer to current and next CIs */
+ u_short cilen, citype; /* Parsed len, type */
+#if VJ_SUPPORT
+ u_short cishort; /* Parsed short value */
+#endif /* VJ_SUPPORT */
+ u32_t tl, ciaddr1, ciaddr2;/* Parsed address values */
+ 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 */
+#if VJ_SUPPORT
+ u_char maxslotindex, cflag;
+#endif /* VJ_SUPPORT */
+#if LWIP_DNS
+ int d;
+#endif /* LWIP_DNS */
+
+ /*
+ * 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(("ipcp_reqci: bad CI length!"));
+ orc = CONFREJ; /* Reject bad CI */
+ cilen = 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 */
+ case CI_ADDRS:
+ if (!ao->old_addrs || ho->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 = lwip_htonl(tl);
+ if (ciaddr1 != wo->hisaddr
+ && (ciaddr1 == 0 || !wo->accept_remote)) {
+ orc = CONFNAK;
+ if (!reject_if_disagree) {
+ DECPTR(sizeof(u32_t), p);
+ tl = lwip_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 = lwip_htonl(tl);
+ if (ciaddr2 != wo->ouraddr) {
+ if (ciaddr2 == 0 || !wo->accept_local) {
+ orc = CONFNAK;
+ if (!reject_if_disagree) {
+ DECPTR(sizeof(u32_t), p);
+ tl = lwip_ntohl(wo->ouraddr);
+ PUTLONG(tl, p);
+ }
+ } else {
+ wo->ouraddr = ciaddr2; /* accept peer's idea */
+ }
+ }
+
+ ho->old_addrs = 1;
+ ho->hisaddr = ciaddr1;
+ ho->ouraddr = ciaddr2;
+ break;
+
+ case CI_ADDR:
+ if (!ao->neg_addr || ho->old_addrs ||
+ cilen != CILEN_ADDR) { /* 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 = lwip_htonl(tl);
+ if (ciaddr1 != wo->hisaddr
+ && (ciaddr1 == 0 || !wo->accept_remote)) {
+ orc = CONFNAK;
+ if (!reject_if_disagree) {
+ DECPTR(sizeof(u32_t), p);
+ tl = lwip_ntohl(wo->hisaddr);
+ PUTLONG(tl, p);
+ }
+ } else if (ciaddr1 == 0 && wo->hisaddr == 0) {
+ /*
+ * Don't ACK an address of 0.0.0.0 - reject it instead.
+ */
+ orc = CONFREJ;
+ wo->req_addr = 0; /* don't NAK with 0.0.0.0 later */
+ break;
+ }
+
+ ho->neg_addr = 1;
+ ho->hisaddr = ciaddr1;
+ break;
+
+#if LWIP_DNS
+ 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 */
+ orc = CONFREJ; /* Reject CI */
+ break;
+ }
+ GETLONG(tl, p);
+ if (lwip_htonl(tl) != ao->dnsaddr[d]) {
+ DECPTR(sizeof(u32_t), p);
+ tl = lwip_ntohl(ao->dnsaddr[d]);
+ PUTLONG(tl, p);
+ orc = CONFNAK;
+ }
+ break;
+#endif /* LWIP_DNS */
+
+#if 0 /* UNUSED - WINS */
+ case CI_MS_WINS1:
+ case CI_MS_WINS2:
+ /* Microsoft primary or secondary WINS request */
+ d = citype == CI_MS_WINS2;
+
+ /* 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 (lwip_htonl(tl) != ao->winsaddr[d]) {
+ DECPTR(sizeof(u32_t), p);
+ tl = lwip_ntohl(ao->winsaddr[d]);
+ PUTLONG(tl, p);
+ orc = CONFNAK;
+ }
+ break;
+#endif /* UNUSED - WINS */
+
+#if VJ_SUPPORT
+ case CI_COMPRESSTYPE:
+ if (!ao->neg_vj ||
+ (cilen != CILEN_VJ && cilen != CILEN_COMPRESS)) {
+ orc = CONFREJ;
+ break;
+ }
+ GETSHORT(cishort, p);
+
+ if (!(cishort == IPCP_VJ_COMP ||
+ (cishort == IPCP_VJ_COMP_OLD && cilen == CILEN_COMPRESS))) {
+ orc = CONFREJ;
+ break;
+ }
+
+ ho->neg_vj = 1;
+ ho->vj_protocol = cishort;
+ if (cilen == CILEN_VJ) {
+ GETCHAR(maxslotindex, p);
+ if (maxslotindex > ao->maxslotindex) {
+ orc = CONFNAK;
+ if (!reject_if_disagree){
+ DECPTR(1, p);
+ PUTCHAR(ao->maxslotindex, p);
+ }
+ }
+ GETCHAR(cflag, p);
+ if (cflag && !ao->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_STATES - 1;
+ ho->cflag = 1;
+ }
+ break;
+#endif /* VJ_SUPPORT */
+
+ default:
+ 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? */
+ 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)
+ MEMCPY(ucp, cip, 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 && !ho->old_addrs &&
+ wo->req_addr && !reject_if_disagree && !pcb->settings.noremoteip) {
+ 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 = lwip_ntohl(wo->hisaddr);
+ PUTLONG(tl, ucp);
+ }
+
+ *len = ucp - inp; /* Compute output length */
+ IPCPDEBUG(("ipcp: returning Configure-%s", CODENAME(rc)));
+ return (rc); /* Return final code */
+}
+
+
+#if 0 /* UNUSED */
+/*
+ * ip_check_options - check that any IP-related options are OK,
+ * and assign appropriate defaults.
+ */
+static void
+ip_check_options()
+{
+ struct hostent *hp;
+ u32_t local;
+ ipcp_options *wo = &ipcp_wantoptions[0];
+
+ /*
+ * Default our local IP address based on our hostname.
+ * If local IP address already given, don't bother.
+ */
+ if (wo->ouraddr == 0 && !disable_defaultip) {
+ /*
+ * Look up our hostname (possibly with domain name appended)
+ * and take the first IP address as our local IP address.
+ * If there isn't an IP address for our hostname, too bad.
+ */
+ wo->accept_local = 1; /* don't insist on this default value */
+ if ((hp = gethostbyname(hostname)) != NULL) {
+ local = *(u32_t *)hp->h_addr;
+ if (local != 0 && !bad_ip_adrs(local))
+ wo->ouraddr = local;
+ }
+ }
+ ask_for_local = wo->ouraddr != 0 || !disable_defaultip;
+}
+#endif /* UNUSED */
+
+#if DEMAND_SUPPORT
+/*
+ * ip_demand_conf - configure the interface as though
+ * IPCP were up, for use with dial-on-demand.
+ */
+static int
+ip_demand_conf(u)
+ int u;
+{
+ ppp_pcb *pcb = &ppp_pcb_list[u];
+ ipcp_options *wo = &ipcp_wantoptions[u];
+
+ if (wo->hisaddr == 0 && !pcb->settings.noremoteip) {
+ /* make up an arbitrary address for the peer */
+ wo->hisaddr = lwip_htonl(0x0a707070 + ifunit);
+ wo->accept_remote = 1;
+ }
+ if (wo->ouraddr == 0) {
+ /* make up an arbitrary address for us */
+ wo->ouraddr = lwip_htonl(0x0a404040 + ifunit);
+ wo->accept_local = 1;
+ ask_for_local = 0; /* don't tell the peer this address */
+ }
+ if (!sifaddr(pcb, wo->ouraddr, wo->hisaddr, get_mask(wo->ouraddr)))
+ return 0;
+ if (!sifup(pcb))
+ return 0;
+ if (!sifnpmode(pcb, PPP_IP, NPMODE_QUEUE))
+ return 0;
+#if 0 /* UNUSED */
+ if (wo->default_route)
+ if (sifdefaultroute(pcb, wo->ouraddr, wo->hisaddr,
+ wo->replace_default_route))
+ default_route_set[u] = 1;
+#endif /* UNUSED */
+#if 0 /* UNUSED - PROXY ARP */
+ if (wo->proxy_arp)
+ if (sifproxyarp(pcb, wo->hisaddr))
+ proxy_arp_set[u] = 1;
+#endif /* UNUSED - PROXY ARP */
+
+ ppp_notice("local IP address %I", wo->ouraddr);
+ if (wo->hisaddr)
+ ppp_notice("remote IP address %I", wo->hisaddr);
+
+ return 1;
+}
+#endif /* DEMAND_SUPPORT */
+
+/*
+ * ipcp_up - IPCP has come UP.
+ *
+ * Configure the IP network interface appropriately and bring it up.
+ */
+static void ipcp_up(fsm *f) {
+ ppp_pcb *pcb = f->pcb;
+ u32_t mask;
+ ipcp_options *ho = &pcb->ipcp_hisoptions;
+ ipcp_options *go = &pcb->ipcp_gotoptions;
+ ipcp_options *wo = &pcb->ipcp_wantoptions;
+
+ IPCPDEBUG(("ipcp: up"));
+
+ /*
+ * We must have a non-zero IP address for both ends of the link.
+ */
+ if (!ho->neg_addr && !ho->old_addrs)
+ ho->hisaddr = wo->hisaddr;
+
+ if (!(go->neg_addr || go->old_addrs) && (wo->neg_addr || wo->old_addrs)
+ && wo->ouraddr != 0) {
+ ppp_error("Peer refused to agree to our IP address");
+ ipcp_close(f->pcb, "Refused our IP address");
+ return;
+ }
+ if (go->ouraddr == 0) {
+ ppp_error("Could not determine local IP address");
+ ipcp_close(f->pcb, "Could not determine local IP address");
+ return;
+ }
+ if (ho->hisaddr == 0 && !pcb->settings.noremoteip) {
+ ho->hisaddr = lwip_htonl(0x0a404040);
+ ppp_warn("Could not determine remote IP address: defaulting to %I",
+ ho->hisaddr);
+ }
+#if 0 /* UNUSED */
+ script_setenv("IPLOCAL", ip_ntoa(go->ouraddr), 0);
+ if (ho->hisaddr != 0)
+ script_setenv("IPREMOTE", ip_ntoa(ho->hisaddr), 1);
+#endif /* UNUSED */
+
+#if LWIP_DNS
+ if (!go->req_dns1)
+ go->dnsaddr[0] = 0;
+ if (!go->req_dns2)
+ go->dnsaddr[1] = 0;
+#if 0 /* UNUSED */
+ if (go->dnsaddr[0])
+ script_setenv("DNS1", ip_ntoa(go->dnsaddr[0]), 0);
+ if (go->dnsaddr[1])
+ script_setenv("DNS2", ip_ntoa(go->dnsaddr[1]), 0);
+#endif /* UNUSED */
+ if (pcb->settings.usepeerdns && (go->dnsaddr[0] || go->dnsaddr[1])) {
+ sdns(pcb, go->dnsaddr[0], go->dnsaddr[1]);
+#if 0 /* UNUSED */
+ script_setenv("USEPEERDNS", "1", 0);
+ create_resolv(go->dnsaddr[0], go->dnsaddr[1]);
+#endif /* UNUSED */
+ }
+#endif /* LWIP_DNS */
+
+ /*
+ * Check that the peer is allowed to use the IP address it wants.
+ */
+ if (ho->hisaddr != 0) {
+ u32_t addr = lwip_ntohl(ho->hisaddr);
+ if ((addr >> IP_CLASSA_NSHIFT) == IP_LOOPBACKNET
+ || IP_MULTICAST(addr) || IP_BADCLASS(addr)
+ /*
+ * For now, consider that PPP in server mode with peer required
+ * to authenticate must provide the peer IP address, reject any
+ * IP address wanted by peer different than the one we wanted.
+ */
+#if PPP_SERVER && PPP_AUTH_SUPPORT
+ || (pcb->settings.auth_required && wo->hisaddr != ho->hisaddr)
+#endif /* PPP_SERVER && PPP_AUTH_SUPPORT */
+ ) {
+ ppp_error("Peer is not authorized to use remote address %I", ho->hisaddr);
+ ipcp_close(pcb, "Unauthorized remote IP address");
+ return;
+ }
+ }
+#if 0 /* Unused */
+ /* Upstream checking code */
+ if (ho->hisaddr != 0 && !auth_ip_addr(f->unit, ho->hisaddr)) {
+ ppp_error("Peer is not authorized to use remote address %I", ho->hisaddr);
+ ipcp_close(f->unit, "Unauthorized remote IP address");
+ return;
+ }
+#endif /* Unused */
+
+#if VJ_SUPPORT
+ /* set tcp compression */
+ sifvjcomp(pcb, ho->neg_vj, ho->cflag, ho->maxslotindex);
+#endif /* VJ_SUPPORT */
+
+#if DEMAND_SUPPORT
+ /*
+ * If we are doing dial-on-demand, the interface is already
+ * configured, so we put out any saved-up packets, then set the
+ * interface to pass IP packets.
+ */
+ if (demand) {
+ if (go->ouraddr != wo->ouraddr || ho->hisaddr != wo->hisaddr) {
+ ipcp_clear_addrs(f->unit, wo->ouraddr, wo->hisaddr,
+ wo->replace_default_route);
+ if (go->ouraddr != wo->ouraddr) {
+ ppp_warn("Local IP address changed to %I", go->ouraddr);
+ script_setenv("OLDIPLOCAL", ip_ntoa(wo->ouraddr), 0);
+ wo->ouraddr = go->ouraddr;
+ } else
+ script_unsetenv("OLDIPLOCAL");
+ if (ho->hisaddr != wo->hisaddr && wo->hisaddr != 0) {
+ ppp_warn("Remote IP address changed to %I", ho->hisaddr);
+ script_setenv("OLDIPREMOTE", ip_ntoa(wo->hisaddr), 0);
+ wo->hisaddr = ho->hisaddr;
+ } else
+ script_unsetenv("OLDIPREMOTE");
+
+ /* Set the interface to the new addresses */
+ mask = get_mask(go->ouraddr);
+ if (!sifaddr(pcb, go->ouraddr, ho->hisaddr, mask)) {
+#if PPP_DEBUG
+ ppp_warn("Interface configuration failed");
+#endif /* PPP_DEBUG */
+ ipcp_close(f->unit, "Interface configuration failed");
+ return;
+ }
+
+ /* assign a default route through the interface if required */
+ if (ipcp_wantoptions[f->unit].default_route)
+ if (sifdefaultroute(pcb, go->ouraddr, ho->hisaddr,
+ wo->replace_default_route))
+ default_route_set[f->unit] = 1;
+
+#if 0 /* UNUSED - PROXY ARP */
+ /* Make a proxy ARP entry if requested. */
+ if (ho->hisaddr != 0 && ipcp_wantoptions[f->unit].proxy_arp)
+ if (sifproxyarp(pcb, ho->hisaddr))
+ proxy_arp_set[f->unit] = 1;
+#endif /* UNUSED - PROXY ARP */
+
+ }
+ demand_rexmit(PPP_IP,go->ouraddr);
+ sifnpmode(pcb, PPP_IP, NPMODE_PASS);
+
+ } else
+#endif /* DEMAND_SUPPORT */
+ {
+ /*
+ * Set IP addresses and (if specified) netmask.
+ */
+ mask = get_mask(go->ouraddr);
+
+#if !(defined(SVR4) && (defined(SNI) || defined(__USLC__)))
+ if (!sifaddr(pcb, go->ouraddr, ho->hisaddr, mask)) {
+#if PPP_DEBUG
+ ppp_warn("Interface configuration failed");
+#endif /* PPP_DEBUG */
+ ipcp_close(f->pcb, "Interface configuration failed");
+ return;
+ }
+#endif
+
+ /* bring the interface up for IP */
+ if (!sifup(pcb)) {
+#if PPP_DEBUG
+ ppp_warn("Interface failed to come up");
+#endif /* PPP_DEBUG */
+ ipcp_close(f->pcb, "Interface configuration failed");
+ return;
+ }
+
+#if (defined(SVR4) && (defined(SNI) || defined(__USLC__)))
+ if (!sifaddr(pcb, go->ouraddr, ho->hisaddr, mask)) {
+#if PPP_DEBUG
+ ppp_warn("Interface configuration failed");
+#endif /* PPP_DEBUG */
+ ipcp_close(f->unit, "Interface configuration failed");
+ return;
+ }
+#endif
+#if DEMAND_SUPPORT
+ sifnpmode(pcb, PPP_IP, NPMODE_PASS);
+#endif /* DEMAND_SUPPORT */
+
+#if 0 /* UNUSED */
+ /* assign a default route through the interface if required */
+ if (wo->default_route)
+ if (sifdefaultroute(pcb, go->ouraddr, ho->hisaddr,
+ wo->replace_default_route))
+ pcb->default_route_set = 1;
+#endif /* UNUSED */
+
+#if 0 /* UNUSED - PROXY ARP */
+ /* Make a proxy ARP entry if requested. */
+ if (ho->hisaddr != 0 && wo->proxy_arp)
+ if (sifproxyarp(pcb, ho->hisaddr))
+ pcb->proxy_arp_set = 1;
+#endif /* UNUSED - PROXY ARP */
+
+ wo->ouraddr = go->ouraddr;
+
+ ppp_notice("local IP address %I", go->ouraddr);
+ if (ho->hisaddr != 0)
+ ppp_notice("remote IP address %I", ho->hisaddr);
+#if LWIP_DNS
+ if (go->dnsaddr[0])
+ ppp_notice("primary DNS address %I", go->dnsaddr[0]);
+ if (go->dnsaddr[1])
+ ppp_notice("secondary DNS address %I", go->dnsaddr[1]);
+#endif /* LWIP_DNS */
+ }
+
+#if PPP_STATS_SUPPORT
+ reset_link_stats(f->unit);
+#endif /* PPP_STATS_SUPPORT */
+
+ np_up(pcb, PPP_IP);
+ pcb->ipcp_is_up = 1;
+
+#if PPP_NOTIFY
+ notify(ip_up_notifier, 0);
+#endif /* PPP_NOTIFY */
+#if 0 /* UNUSED */
+ if (ip_up_hook)
+ ip_up_hook();
+#endif /* UNUSED */
+}
+
+
+/*
+ * 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) {
+ ppp_pcb *pcb = f->pcb;
+ ipcp_options *ho = &pcb->ipcp_hisoptions;
+ ipcp_options *go = &pcb->ipcp_gotoptions;
+
+ IPCPDEBUG(("ipcp: down"));
+#if PPP_STATS_SUPPORT
+ /* XXX a bit IPv4-centric here, we only need to get the stats
+ * before the interface is marked down. */
+ /* XXX more correct: we must get the stats before running the notifiers,
+ * at least for the radius plugin */
+ update_link_stats(f->unit);
+#endif /* PPP_STATS_SUPPORT */
+#if PPP_NOTIFY
+ notify(ip_down_notifier, 0);
+#endif /* PPP_NOTIFY */
+#if 0 /* UNUSED */
+ if (ip_down_hook)
+ ip_down_hook();
+#endif /* UNUSED */
+ if (pcb->ipcp_is_up) {
+ pcb->ipcp_is_up = 0;
+ np_down(pcb, PPP_IP);
+ }
+#if VJ_SUPPORT
+ sifvjcomp(pcb, 0, 0, 0);
+#endif /* VJ_SUPPORT */
+
+#if PPP_STATS_SUPPORT
+ print_link_stats(); /* _after_ running the notifiers and ip_down_hook(),
+ * because print_link_stats() sets link_stats_valid
+ * to 0 (zero) */
+#endif /* PPP_STATS_SUPPORT */
+
+#if DEMAND_SUPPORT
+ /*
+ * If we are doing dial-on-demand, set the interface
+ * to queue up outgoing packets (for now).
+ */
+ if (demand) {
+ sifnpmode(pcb, PPP_IP, NPMODE_QUEUE);
+ } else
+#endif /* DEMAND_SUPPORT */
+ {
+#if DEMAND_SUPPORT
+ sifnpmode(pcb, PPP_IP, NPMODE_DROP);
+#endif /* DEMAND_SUPPORT */
+ sifdown(pcb);
+ ipcp_clear_addrs(pcb, go->ouraddr,
+ ho->hisaddr, 0);
+#if LWIP_DNS
+ cdns(pcb, go->dnsaddr[0], go->dnsaddr[1]);
+#endif /* LWIP_DNS */
+ }
+}
+
+
+/*
+ * ipcp_clear_addrs() - clear the interface addresses, routes,
+ * proxy arp entries, etc.
+ */
+static void ipcp_clear_addrs(ppp_pcb *pcb, u32_t ouraddr, u32_t hisaddr, u8_t replacedefaultroute) {
+ LWIP_UNUSED_ARG(replacedefaultroute);
+
+#if 0 /* UNUSED - PROXY ARP */
+ if (pcb->proxy_arp_set) {
+ cifproxyarp(pcb, hisaddr);
+ pcb->proxy_arp_set = 0;
+ }
+#endif /* UNUSED - PROXY ARP */
+#if 0 /* UNUSED */
+ /* If replacedefaultroute, sifdefaultroute will be called soon
+ * with replacedefaultroute set and that will overwrite the current
+ * default route. This is the case only when doing demand, otherwise
+ * during demand, this cifdefaultroute would restore the old default
+ * route which is not what we want in this case. In the non-demand
+ * case, we'll delete the default route and restore the old if there
+ * is one saved by an sifdefaultroute with replacedefaultroute.
+ */
+ if (!replacedefaultroute && pcb->default_route_set) {
+ cifdefaultroute(pcb, ouraddr, hisaddr);
+ pcb->default_route_set = 0;
+ }
+#endif /* UNUSED */
+ cifaddr(pcb, ouraddr, hisaddr);
+}
+
+
+/*
+ * ipcp_finished - possibly shut down the lower layers.
+ */
+static void ipcp_finished(fsm *f) {
+ ppp_pcb *pcb = f->pcb;
+ if (pcb->ipcp_is_open) {
+ pcb->ipcp_is_open = 0;
+ np_finished(pcb, PPP_IP);
+ }
+}
+
+
+#if 0 /* UNUSED */
+/*
+ * create_resolv - create the replacement resolv.conf file
+ */
+static void
+create_resolv(peerdns1, peerdns2)
+ u32_t peerdns1, peerdns2;
+{
+
+}
+#endif /* UNUSED */
+
+#if PRINTPKT_SUPPORT
+/*
+ * ipcp_printpkt - print the contents of an IPCP packet.
+ */
+static const char* const ipcp_codenames[] = {
+ "ConfReq", "ConfAck", "ConfNak", "ConfRej",
+ "TermReq", "TermAck", "CodeRej"
+};
+
+static int ipcp_printpkt(const u_char *p, int plen,
+ void (*printer) (void *, const char *, ...), void *arg) {
+ int code, id, len, olen;
+ const u_char *pstart, *optend;
+#if VJ_SUPPORT
+ u_short cishort;
+#endif /* VJ_SUPPORT */
+ 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 <= (int)LWIP_ARRAYSIZE(ipcp_codenames))
+ printer(arg, " %s", ipcp_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_ADDRS:
+ if (olen == CILEN_ADDRS) {
+ p += 2;
+ GETLONG(cilong, p);
+ printer(arg, "addrs %I", lwip_htonl(cilong));
+ GETLONG(cilong, p);
+ printer(arg, " %I", lwip_htonl(cilong));
+ }
+ break;
+#if VJ_SUPPORT
+ case CI_COMPRESSTYPE:
+ if (olen >= CILEN_COMPRESS) {
+ p += 2;
+ GETSHORT(cishort, p);
+ printer(arg, "compress ");
+ switch (cishort) {
+ case IPCP_VJ_COMP:
+ printer(arg, "VJ");
+ break;
+ case IPCP_VJ_COMP_OLD:
+ printer(arg, "old-VJ");
+ break;
+ default:
+ printer(arg, "0x%x", cishort);
+ }
+ }
+ break;
+#endif /* VJ_SUPPORT */
+ case CI_ADDR:
+ if (olen == CILEN_ADDR) {
+ p += 2;
+ GETLONG(cilong, p);
+ printer(arg, "addr %I", lwip_htonl(cilong));
+ }
+ break;
+#if LWIP_DNS
+ case CI_MS_DNS1:
+ case CI_MS_DNS2:
+ p += 2;
+ GETLONG(cilong, p);
+ printer(arg, "ms-dns%d %I", (code == CI_MS_DNS1? 1: 2),
+ htonl(cilong));
+ break;
+#endif /* LWIP_DNS */
+#if 0 /* UNUSED - WINS */
+ case CI_MS_WINS1:
+ case CI_MS_WINS2:
+ p += 2;
+ GETLONG(cilong, p);
+ printer(arg, "ms-wins %I", lwip_htonl(cilong));
+ break;
+#endif /* UNUSED - WINS */
+ default:
+ 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, " ");
+ ppp_print_string(p, len, printer, arg);
+ p += len;
+ len = 0;
+ }
+ break;
+ default:
+ break;
+ }
+
+ /* print the rest of the bytes in the packet */
+ for (; len > 0; --len) {
+ GETCHAR(code, p);
+ printer(arg, " %.2x", code);
+ }
+
+ return p - pstart;
+}
+#endif /* PRINTPKT_SUPPORT */
+
+#if DEMAND_SUPPORT
+/*
+ * 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
+#ifndef IPPROTO_TCP
+#define IPPROTO_TCP 6
+#endif
+#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(pkt, len)
+ 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 /* DEMAND_SUPPORT */
+
+#endif /* PPP_SUPPORT && PPP_IPV4_SUPPORT */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/ipv6cp.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/ipv6cp.c
new file mode 100644
index 0000000..11c18df
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/ipv6cp.c
@@ -0,0 +1,1533 @@
+/*
+ * ipv6cp.c - PPP IPV6 Control Protocol.
+ *
+ * Copyright (c) 1999 Tommi Komulainen. 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(s) of the authors of this software must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission.
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by Tommi Komulainen
+ * <Tommi.Komulainen@iki.fi>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+/* Original version, based on RFC2023 :
+
+ Copyright (c) 1995, 1996, 1997 Francis.Dupont@inria.fr, INRIA Rocquencourt,
+ Alain.Durand@imag.fr, IMAG,
+ Jean-Luc.Richier@imag.fr, IMAG-LSR.
+
+ Copyright (c) 1998, 1999 Francis.Dupont@inria.fr, GIE DYADE,
+ Alain.Durand@imag.fr, IMAG,
+ Jean-Luc.Richier@imag.fr, IMAG-LSR.
+
+ Ce travail a été fait au sein du GIE DYADE (Groupement d'Intérêt
+ Économique ayant pour membres BULL S.A. et l'INRIA).
+
+ Ce logiciel informatique est disponible aux conditions
+ usuelles dans la recherche, c'est-à-dire qu'il peut
+ être utilisé, copié, modifié, distribué à l'unique
+ condition que ce texte soit conservé afin que
+ l'origine de ce logiciel soit reconnue.
+
+ Le nom de l'Institut National de Recherche en Informatique
+ et en Automatique (INRIA), de l'IMAG, ou d'une personne morale
+ ou physique ayant participé à l'élaboration de ce logiciel ne peut
+ être utilisé sans son accord préalable explicite.
+
+ Ce logiciel est fourni tel quel sans aucune garantie,
+ support ou responsabilité d'aucune sorte.
+ Ce logiciel est dérivé de sources d'origine
+ "University of California at Berkeley" et
+ "Digital Equipment Corporation" couvertes par des copyrights.
+
+ L'Institut d'Informatique et de Mathématiques Appliquées de Grenoble (IMAG)
+ est une fédération d'unités mixtes de recherche du CNRS, de l'Institut National
+ Polytechnique de Grenoble et de l'Université Joseph Fourier regroupant
+ sept laboratoires dont le laboratoire Logiciels, Systèmes, Réseaux (LSR).
+
+ This work has been done in the context of GIE DYADE (joint R & D venture
+ between BULL S.A. and INRIA).
+
+ This software is available with usual "research" terms
+ with the aim of retain credits of the software.
+ Permission to use, copy, modify and distribute this software for any
+ purpose and without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies,
+ and the name of INRIA, IMAG, or any contributor not be used in advertising
+ or publicity pertaining to this material without the prior explicit
+ permission. The software is provided "as is" without any
+ warranties, support or liabilities of any kind.
+ This software is derived from source code from
+ "University of California at Berkeley" and
+ "Digital Equipment Corporation" protected by copyrights.
+
+ Grenoble's Institute of Computer Science and Applied Mathematics (IMAG)
+ is a federation of seven research units funded by the CNRS, National
+ Polytechnic Institute of Grenoble and University Joseph Fourier.
+ The research unit in Software, Systems, Networks (LSR) is member of IMAG.
+*/
+
+/*
+ * Derived from :
+ *
+ *
+ * ipcp.c - PPP IP Control Protocol.
+ *
+ * Copyright (c) 1984-2000 Carnegie Mellon University. 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 "Carnegie Mellon University" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission. For permission or any legal
+ * details, please contact
+ * Office of Technology Transfer
+ * Carnegie Mellon University
+ * 5000 Forbes Avenue
+ * Pittsburgh, PA 15213-3890
+ * (412) 268-4387, fax: (412) 268-7395
+ * tech-transfer@andrew.cmu.edu
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by Computing Services
+ * at Carnegie Mellon University (http://www.cmu.edu/computing/)."
+ *
+ * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
+ * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $Id: ipv6cp.c,v 1.21 2005/08/25 23:59:34 paulus Exp $
+ */
+
+/*
+ * @todo:
+ *
+ * Proxy Neighbour Discovery.
+ *
+ * Better defines for selecting the ordering of
+ * interface up / set address.
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT && PPP_IPV6_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#if 0 /* UNUSED */
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#endif /* UNUSED */
+
+#include "netif/ppp/ppp_impl.h"
+#include "netif/ppp/fsm.h"
+#include "netif/ppp/ipcp.h"
+#include "netif/ppp/ipv6cp.h"
+#include "netif/ppp/magic.h"
+
+/* global vars */
+#if 0 /* UNUSED */
+int no_ifaceid_neg = 0;
+#endif /* UNUSED */
+
+/*
+ * Callbacks for fsm code. (CI = Configuration Information)
+ */
+static void ipv6cp_resetci(fsm *f); /* Reset our CI */
+static int ipv6cp_cilen(fsm *f); /* Return length of our CI */
+static void ipv6cp_addci(fsm *f, u_char *ucp, int *lenp); /* Add our CI */
+static int ipv6cp_ackci(fsm *f, u_char *p, int len); /* Peer ack'd our CI */
+static int ipv6cp_nakci(fsm *f, u_char *p, int len, int treat_as_reject); /* Peer nak'd our CI */
+static int ipv6cp_rejci(fsm *f, u_char *p, int len); /* Peer rej'd our CI */
+static int ipv6cp_reqci(fsm *f, u_char *inp, int *len, int reject_if_disagree); /* Rcv CI */
+static void ipv6cp_up(fsm *f); /* We're UP */
+static void ipv6cp_down(fsm *f); /* We're DOWN */
+static void ipv6cp_finished(fsm *f); /* Don't need lower layer */
+
+static const fsm_callbacks ipv6cp_callbacks = { /* IPV6CP callback routines */
+ ipv6cp_resetci, /* Reset our Configuration Information */
+ ipv6cp_cilen, /* Length of our Configuration Information */
+ ipv6cp_addci, /* Add our Configuration Information */
+ ipv6cp_ackci, /* ACK our Configuration Information */
+ ipv6cp_nakci, /* NAK our Configuration Information */
+ ipv6cp_rejci, /* Reject our Configuration Information */
+ ipv6cp_reqci, /* Request peer's Configuration Information */
+ ipv6cp_up, /* Called when fsm reaches OPENED state */
+ ipv6cp_down, /* Called when fsm leaves OPENED state */
+ NULL, /* Called when we want the lower layer up */
+ ipv6cp_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 */
+ "IPV6CP" /* String name of protocol */
+};
+
+#if PPP_OPTIONS
+/*
+ * Command-line options.
+ */
+static int setifaceid(char **arg));
+static void printifaceid(option_t *,
+ void (*)(void *, char *, ...), void *));
+
+static option_t ipv6cp_option_list[] = {
+ { "ipv6", o_special, (void *)setifaceid,
+ "Set interface identifiers for IPV6",
+ OPT_A2PRINTER, (void *)printifaceid },
+
+ { "+ipv6", o_bool, &ipv6cp_protent.enabled_flag,
+ "Enable IPv6 and IPv6CP", OPT_PRIO | 1 },
+ { "noipv6", o_bool, &ipv6cp_protent.enabled_flag,
+ "Disable IPv6 and IPv6CP", OPT_PRIOSUB },
+ { "-ipv6", o_bool, &ipv6cp_protent.enabled_flag,
+ "Disable IPv6 and IPv6CP", OPT_PRIOSUB | OPT_ALIAS },
+
+ { "ipv6cp-accept-local", o_bool, &ipv6cp_allowoptions[0].accept_local,
+ "Accept peer's interface identifier for us", 1 },
+
+ { "ipv6cp-use-ipaddr", o_bool, &ipv6cp_allowoptions[0].use_ip,
+ "Use (default) IPv4 address as interface identifier", 1 },
+
+ { "ipv6cp-use-persistent", o_bool, &ipv6cp_wantoptions[0].use_persistent,
+ "Use uniquely-available persistent value for link local address", 1 },
+
+ { "ipv6cp-restart", o_int, &ipv6cp_fsm[0].timeouttime,
+ "Set timeout for IPv6CP", OPT_PRIO },
+ { "ipv6cp-max-terminate", o_int, &ipv6cp_fsm[0].maxtermtransmits,
+ "Set max #xmits for term-reqs", OPT_PRIO },
+ { "ipv6cp-max-configure", o_int, &ipv6cp_fsm[0].maxconfreqtransmits,
+ "Set max #xmits for conf-reqs", OPT_PRIO },
+ { "ipv6cp-max-failure", o_int, &ipv6cp_fsm[0].maxnakloops,
+ "Set max #conf-naks for IPv6CP", OPT_PRIO },
+
+ { NULL }
+};
+#endif /* PPP_OPTIONS */
+
+/*
+ * Protocol entry points from main code.
+ */
+static void ipv6cp_init(ppp_pcb *pcb);
+static void ipv6cp_open(ppp_pcb *pcb);
+static void ipv6cp_close(ppp_pcb *pcb, const char *reason);
+static void ipv6cp_lowerup(ppp_pcb *pcb);
+static void ipv6cp_lowerdown(ppp_pcb *pcb);
+static void ipv6cp_input(ppp_pcb *pcb, u_char *p, int len);
+static void ipv6cp_protrej(ppp_pcb *pcb);
+#if PPP_OPTIONS
+static void ipv6_check_options(void);
+#endif /* PPP_OPTIONS */
+#if DEMAND_SUPPORT
+static int ipv6_demand_conf(int u);
+#endif /* DEMAND_SUPPORT */
+#if PRINTPKT_SUPPORT
+static int ipv6cp_printpkt(const u_char *p, int plen,
+ void (*printer)(void *, const char *, ...), void *arg);
+#endif /* PRINTPKT_SUPPORT */
+#if DEMAND_SUPPORT
+static int ipv6_active_pkt(u_char *pkt, int len);
+#endif /* DEMAND_SUPPORT */
+
+const struct protent ipv6cp_protent = {
+ PPP_IPV6CP,
+ ipv6cp_init,
+ ipv6cp_input,
+ ipv6cp_protrej,
+ ipv6cp_lowerup,
+ ipv6cp_lowerdown,
+ ipv6cp_open,
+ ipv6cp_close,
+#if PRINTPKT_SUPPORT
+ ipv6cp_printpkt,
+#endif /* PRINTPKT_SUPPORT */
+#if PPP_DATAINPUT
+ NULL,
+#endif /* PPP_DATAINPUT */
+#if PRINTPKT_SUPPORT
+ "IPV6CP",
+ "IPV6",
+#endif /* PRINTPKT_SUPPORT */
+#if PPP_OPTIONS
+ ipv6cp_option_list,
+ ipv6_check_options,
+#endif /* PPP_OPTIONS */
+#if DEMAND_SUPPORT
+ ipv6_demand_conf,
+ ipv6_active_pkt
+#endif /* DEMAND_SUPPORT */
+};
+
+static void ipv6cp_clear_addrs(ppp_pcb *pcb, eui64_t ourid, eui64_t hisid);
+#if 0 /* UNUSED */
+static void ipv6cp_script(char *));
+static void ipv6cp_script_done(void *));
+#endif /* UNUSED */
+
+/*
+ * Lengths of configuration options.
+ */
+#define CILEN_VOID 2
+#define CILEN_COMPRESS 4 /* length for RFC2023 compress opt. */
+#define CILEN_IFACEID 10 /* RFC2472, interface identifier */
+
+#define CODENAME(x) ((x) == CONFACK ? "ACK" : \
+ (x) == CONFNAK ? "NAK" : "REJ")
+
+#if 0 /* UNUSED */
+/*
+ * This state variable is used to ensure that we don't
+ * run an ipcp-up/down script while one is already running.
+ */
+static enum script_state {
+ s_down,
+ s_up,
+} ipv6cp_script_state;
+static pid_t ipv6cp_script_pid;
+#endif /* UNUSED */
+
+static char *llv6_ntoa(eui64_t ifaceid);
+
+#if PPP_OPTIONS
+/*
+ * setifaceid - set the interface identifiers manually
+ */
+static int
+setifaceid(argv)
+ char **argv;
+{
+ char *comma, *arg, c;
+ ipv6cp_options *wo = &ipv6cp_wantoptions[0];
+ struct in6_addr addr;
+ static int prio_local, prio_remote;
+
+#define VALIDID(a) ( (((a).s6_addr32[0] == 0) && ((a).s6_addr32[1] == 0)) && \
+ (((a).s6_addr32[2] != 0) || ((a).s6_addr32[3] != 0)) )
+
+ arg = *argv;
+ if ((comma = strchr(arg, ',')) == NULL)
+ comma = arg + strlen(arg);
+
+ /*
+ * If comma first character, then no local identifier
+ */
+ if (comma != arg) {
+ c = *comma;
+ *comma = '\0';
+
+ if (inet_pton(AF_INET6, arg, &addr) == 0 || !VALIDID(addr)) {
+ option_error("Illegal interface identifier (local): %s", arg);
+ return 0;
+ }
+
+ if (option_priority >= prio_local) {
+ eui64_copy(addr.s6_addr32[2], wo->ourid);
+ wo->opt_local = 1;
+ prio_local = option_priority;
+ }
+ *comma = c;
+ }
+
+ /*
+ * If comma last character, the no remote identifier
+ */
+ if (*comma != 0 && *++comma != '\0') {
+ if (inet_pton(AF_INET6, comma, &addr) == 0 || !VALIDID(addr)) {
+ option_error("Illegal interface identifier (remote): %s", comma);
+ return 0;
+ }
+ if (option_priority >= prio_remote) {
+ eui64_copy(addr.s6_addr32[2], wo->hisid);
+ wo->opt_remote = 1;
+ prio_remote = option_priority;
+ }
+ }
+
+ if (override_value("+ipv6", option_priority, option_source))
+ ipv6cp_protent.enabled_flag = 1;
+ return 1;
+}
+
+static void
+printifaceid(opt, printer, arg)
+ option_t *opt;
+ void (*printer)(void *, char *, ...));
+ void *arg;
+{
+ ipv6cp_options *wo = &ipv6cp_wantoptions[0];
+
+ if (wo->opt_local)
+ printer(arg, "%s", llv6_ntoa(wo->ourid));
+ printer(arg, ",");
+ if (wo->opt_remote)
+ printer(arg, "%s", llv6_ntoa(wo->hisid));
+}
+#endif /* PPP_OPTIONS */
+
+/*
+ * Make a string representation of a network address.
+ */
+static char *
+llv6_ntoa(eui64_t ifaceid)
+{
+ static char b[26];
+
+ sprintf(b, "fe80::%02x%02x:%02x%02x:%02x%02x:%02x%02x",
+ ifaceid.e8[0], ifaceid.e8[1], ifaceid.e8[2], ifaceid.e8[3],
+ ifaceid.e8[4], ifaceid.e8[5], ifaceid.e8[6], ifaceid.e8[7]);
+
+ return b;
+}
+
+
+/*
+ * ipv6cp_init - Initialize IPV6CP.
+ */
+static void ipv6cp_init(ppp_pcb *pcb) {
+ fsm *f = &pcb->ipv6cp_fsm;
+ ipv6cp_options *wo = &pcb->ipv6cp_wantoptions;
+ ipv6cp_options *ao = &pcb->ipv6cp_allowoptions;
+
+ f->pcb = pcb;
+ f->protocol = PPP_IPV6CP;
+ f->callbacks = &ipv6cp_callbacks;
+ fsm_init(f);
+
+#if 0 /* Not necessary, everything is cleared in ppp_new() */
+ memset(wo, 0, sizeof(*wo));
+ memset(ao, 0, sizeof(*ao));
+#endif /* 0 */
+
+ wo->accept_local = 1;
+ wo->neg_ifaceid = 1;
+ ao->neg_ifaceid = 1;
+
+#ifdef IPV6CP_COMP
+ wo->neg_vj = 1;
+ ao->neg_vj = 1;
+ wo->vj_protocol = IPV6CP_COMP;
+#endif
+
+}
+
+
+/*
+ * ipv6cp_open - IPV6CP is allowed to come up.
+ */
+static void ipv6cp_open(ppp_pcb *pcb) {
+ fsm_open(&pcb->ipv6cp_fsm);
+}
+
+
+/*
+ * ipv6cp_close - Take IPV6CP down.
+ */
+static void ipv6cp_close(ppp_pcb *pcb, const char *reason) {
+ fsm_close(&pcb->ipv6cp_fsm, reason);
+}
+
+
+/*
+ * ipv6cp_lowerup - The lower layer is up.
+ */
+static void ipv6cp_lowerup(ppp_pcb *pcb) {
+ fsm_lowerup(&pcb->ipv6cp_fsm);
+}
+
+
+/*
+ * ipv6cp_lowerdown - The lower layer is down.
+ */
+static void ipv6cp_lowerdown(ppp_pcb *pcb) {
+ fsm_lowerdown(&pcb->ipv6cp_fsm);
+}
+
+
+/*
+ * ipv6cp_input - Input IPV6CP packet.
+ */
+static void ipv6cp_input(ppp_pcb *pcb, u_char *p, int len) {
+ fsm_input(&pcb->ipv6cp_fsm, p, len);
+}
+
+
+/*
+ * ipv6cp_protrej - A Protocol-Reject was received for IPV6CP.
+ *
+ * Pretend the lower layer went down, so we shut up.
+ */
+static void ipv6cp_protrej(ppp_pcb *pcb) {
+ fsm_lowerdown(&pcb->ipv6cp_fsm);
+}
+
+
+/*
+ * ipv6cp_resetci - Reset our CI.
+ */
+static void ipv6cp_resetci(fsm *f) {
+ ppp_pcb *pcb = f->pcb;
+ ipv6cp_options *wo = &pcb->ipv6cp_wantoptions;
+ ipv6cp_options *go = &pcb->ipv6cp_gotoptions;
+ ipv6cp_options *ao = &pcb->ipv6cp_allowoptions;
+
+ wo->req_ifaceid = wo->neg_ifaceid && ao->neg_ifaceid;
+
+ if (!wo->opt_local) {
+ eui64_magic_nz(wo->ourid);
+ }
+
+ *go = *wo;
+ eui64_zero(go->hisid); /* last proposed interface identifier */
+}
+
+
+/*
+ * ipv6cp_cilen - Return length of our CI.
+ */
+static int ipv6cp_cilen(fsm *f) {
+ ppp_pcb *pcb = f->pcb;
+ ipv6cp_options *go = &pcb->ipv6cp_gotoptions;
+
+#ifdef IPV6CP_COMP
+#define LENCIVJ(neg) (neg ? CILEN_COMPRESS : 0)
+#endif /* IPV6CP_COMP */
+#define LENCIIFACEID(neg) (neg ? CILEN_IFACEID : 0)
+
+ return (LENCIIFACEID(go->neg_ifaceid) +
+#ifdef IPV6CP_COMP
+ LENCIVJ(go->neg_vj) +
+#endif /* IPV6CP_COMP */
+ 0);
+}
+
+
+/*
+ * ipv6cp_addci - Add our desired CIs to a packet.
+ */
+static void ipv6cp_addci(fsm *f, u_char *ucp, int *lenp) {
+ ppp_pcb *pcb = f->pcb;
+ ipv6cp_options *go = &pcb->ipv6cp_gotoptions;
+ int len = *lenp;
+
+#ifdef IPV6CP_COMP
+#define ADDCIVJ(opt, neg, val) \
+ if (neg) { \
+ int vjlen = CILEN_COMPRESS; \
+ if (len >= vjlen) { \
+ PUTCHAR(opt, ucp); \
+ PUTCHAR(vjlen, ucp); \
+ PUTSHORT(val, ucp); \
+ len -= vjlen; \
+ } else \
+ neg = 0; \
+ }
+#endif /* IPV6CP_COMP */
+
+#define ADDCIIFACEID(opt, neg, val1) \
+ if (neg) { \
+ int idlen = CILEN_IFACEID; \
+ if (len >= idlen) { \
+ PUTCHAR(opt, ucp); \
+ PUTCHAR(idlen, ucp); \
+ eui64_put(val1, ucp); \
+ len -= idlen; \
+ } else \
+ neg = 0; \
+ }
+
+ ADDCIIFACEID(CI_IFACEID, go->neg_ifaceid, go->ourid);
+
+#ifdef IPV6CP_COMP
+ ADDCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol);
+#endif /* IPV6CP_COMP */
+
+ *lenp -= len;
+}
+
+
+/*
+ * ipv6cp_ackci - Ack our CIs.
+ *
+ * Returns:
+ * 0 - Ack was bad.
+ * 1 - Ack was good.
+ */
+static int ipv6cp_ackci(fsm *f, u_char *p, int len) {
+ ppp_pcb *pcb = f->pcb;
+ ipv6cp_options *go = &pcb->ipv6cp_gotoptions;
+ u_short cilen, citype;
+#ifdef IPV6CP_COMP
+ u_short cishort;
+#endif /* IPV6CP_COMP */
+ eui64_t ifaceid;
+
+ /*
+ * 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.
+ */
+
+#ifdef IPV6CP_COMP
+#define ACKCIVJ(opt, neg, val) \
+ if (neg) { \
+ int vjlen = CILEN_COMPRESS; \
+ 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; \
+ }
+#endif /* IPV6CP_COMP */
+
+#define ACKCIIFACEID(opt, neg, val1) \
+ if (neg) { \
+ int idlen = CILEN_IFACEID; \
+ if ((len -= idlen) < 0) \
+ goto bad; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != idlen || \
+ citype != opt) \
+ goto bad; \
+ eui64_get(ifaceid, p); \
+ if (! eui64_equals(val1, ifaceid)) \
+ goto bad; \
+ }
+
+ ACKCIIFACEID(CI_IFACEID, go->neg_ifaceid, go->ourid);
+
+#ifdef IPV6CP_COMP
+ ACKCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol);
+#endif /* IPV6CP_COMP */
+
+ /*
+ * If there are any remaining CIs, then this packet is bad.
+ */
+ if (len != 0)
+ goto bad;
+ return (1);
+
+bad:
+ IPV6CPDEBUG(("ipv6cp_ackci: received bad Ack!"));
+ return (0);
+}
+
+/*
+ * ipv6cp_nakci - Peer has sent a NAK for some of our CIs.
+ * This should not modify any state if the Nak is bad
+ * or if IPV6CP is in the OPENED state.
+ *
+ * Returns:
+ * 0 - Nak was bad.
+ * 1 - Nak was good.
+ */
+static int ipv6cp_nakci(fsm *f, u_char *p, int len, int treat_as_reject) {
+ ppp_pcb *pcb = f->pcb;
+ ipv6cp_options *go = &pcb->ipv6cp_gotoptions;
+ u_char citype, cilen, *next;
+#ifdef IPV6CP_COMP
+ u_short cishort;
+#endif /* IPV6CP_COMP */
+ eui64_t ifaceid;
+ ipv6cp_options no; /* options we've seen Naks for */
+ ipv6cp_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 NAKCIIFACEID(opt, neg, code) \
+ if (go->neg && \
+ len >= (cilen = CILEN_IFACEID) && \
+ p[1] == cilen && \
+ p[0] == opt) { \
+ len -= cilen; \
+ INCPTR(2, p); \
+ eui64_get(ifaceid, p); \
+ no.neg = 1; \
+ code \
+ }
+
+#ifdef IPV6CP_COMP
+#define NAKCIVJ(opt, neg, code) \
+ if (go->neg && \
+ ((cilen = p[1]) == CILEN_COMPRESS) && \
+ len >= cilen && \
+ p[0] == opt) { \
+ len -= cilen; \
+ INCPTR(2, p); \
+ GETSHORT(cishort, p); \
+ no.neg = 1; \
+ code \
+ }
+#endif /* IPV6CP_COMP */
+
+ /*
+ * Accept the peer's idea of {our,his} interface identifier, if different
+ * from our idea, only if the accept_{local,remote} flag is set.
+ */
+ NAKCIIFACEID(CI_IFACEID, neg_ifaceid,
+ if (treat_as_reject) {
+ try_.neg_ifaceid = 0;
+ } else if (go->accept_local) {
+ while (eui64_iszero(ifaceid) ||
+ eui64_equals(ifaceid, go->hisid)) /* bad luck */
+ eui64_magic(ifaceid);
+ try_.ourid = ifaceid;
+ IPV6CPDEBUG(("local LL address %s", llv6_ntoa(ifaceid)));
+ }
+ );
+
+#ifdef IPV6CP_COMP
+ NAKCIVJ(CI_COMPRESSTYPE, neg_vj,
+ {
+ if (cishort == IPV6CP_COMP && !treat_as_reject) {
+ try_.vj_protocol = cishort;
+ } else {
+ try_.neg_vj = 0;
+ }
+ }
+ );
+#endif /* IPV6CP_COMP */
+
+ /*
+ * 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 interface identifier, we comply.
+ * If they want us to ask for compression, we refuse.
+ */
+ 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) {
+#ifdef IPV6CP_COMP
+ case CI_COMPRESSTYPE:
+ if (go->neg_vj || no.neg_vj ||
+ (cilen != CILEN_COMPRESS))
+ goto bad;
+ no.neg_vj = 1;
+ break;
+#endif /* IPV6CP_COMP */
+ case CI_IFACEID:
+ if (go->neg_ifaceid || no.neg_ifaceid || cilen != CILEN_IFACEID)
+ goto bad;
+ try_.neg_ifaceid = 1;
+ eui64_get(ifaceid, p);
+ if (go->accept_local) {
+ while (eui64_iszero(ifaceid) ||
+ eui64_equals(ifaceid, go->hisid)) /* bad luck */
+ eui64_magic(ifaceid);
+ try_.ourid = ifaceid;
+ }
+ no.neg_ifaceid = 1;
+ break;
+ default:
+ 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 != PPP_FSM_OPENED)
+ *go = try_;
+
+ return 1;
+
+bad:
+ IPV6CPDEBUG(("ipv6cp_nakci: received bad Nak!"));
+ return 0;
+}
+
+
+/*
+ * ipv6cp_rejci - Reject some of our CIs.
+ */
+static int ipv6cp_rejci(fsm *f, u_char *p, int len) {
+ ppp_pcb *pcb = f->pcb;
+ ipv6cp_options *go = &pcb->ipv6cp_gotoptions;
+ u_char cilen;
+#ifdef IPV6CP_COMP
+ u_short cishort;
+#endif /* IPV6CP_COMP */
+ eui64_t ifaceid;
+ ipv6cp_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 REJCIIFACEID(opt, neg, val1) \
+ if (go->neg && \
+ len >= (cilen = CILEN_IFACEID) && \
+ p[1] == cilen && \
+ p[0] == opt) { \
+ len -= cilen; \
+ INCPTR(2, p); \
+ eui64_get(ifaceid, p); \
+ /* Check rejected value. */ \
+ if (! eui64_equals(ifaceid, val1)) \
+ goto bad; \
+ try_.neg = 0; \
+ }
+
+#ifdef IPV6CP_COMP
+#define REJCIVJ(opt, neg, val) \
+ if (go->neg && \
+ p[1] == CILEN_COMPRESS && \
+ len >= p[1] && \
+ p[0] == opt) { \
+ len -= p[1]; \
+ INCPTR(2, p); \
+ GETSHORT(cishort, p); \
+ /* Check rejected value. */ \
+ if (cishort != val) \
+ goto bad; \
+ try_.neg = 0; \
+ }
+#endif /* IPV6CP_COMP */
+
+ REJCIIFACEID(CI_IFACEID, neg_ifaceid, go->ourid);
+
+#ifdef IPV6CP_COMP
+ REJCIVJ(CI_COMPRESSTYPE, neg_vj, go->vj_protocol);
+#endif /* IPV6CP_COMP */
+
+ /*
+ * If there are any remaining CIs, then this packet is bad.
+ */
+ if (len != 0)
+ goto bad;
+ /*
+ * Now we can update state.
+ */
+ if (f->state != PPP_FSM_OPENED)
+ *go = try_;
+ return 1;
+
+bad:
+ IPV6CPDEBUG(("ipv6cp_rejci: received bad Reject!"));
+ return 0;
+}
+
+
+/*
+ * ipv6cp_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.
+ *
+ * inp = Requested CIs
+ * len = Length of requested CIs
+ *
+ */
+static int ipv6cp_reqci(fsm *f, u_char *inp, int *len, int reject_if_disagree) {
+ ppp_pcb *pcb = f->pcb;
+ ipv6cp_options *wo = &pcb->ipv6cp_wantoptions;
+ ipv6cp_options *ho = &pcb->ipv6cp_hisoptions;
+ ipv6cp_options *ao = &pcb->ipv6cp_allowoptions;
+ ipv6cp_options *go = &pcb->ipv6cp_gotoptions;
+ u_char *cip, *next; /* Pointer to current and next CIs */
+ u_short cilen, citype; /* Parsed len, type */
+#ifdef IPV6CP_COMP
+ u_short cishort; /* Parsed short value */
+#endif /* IPV6CP_COMP */
+ eui64_t ifaceid; /* Parsed interface identifier */
+ 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 */
+
+ /*
+ * 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? */
+ IPV6CPDEBUG(("ipv6cp_reqci: bad CI length!"));
+ orc = CONFREJ; /* Reject bad CI */
+ cilen = 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 */
+ case CI_IFACEID:
+ IPV6CPDEBUG(("ipv6cp: received interface identifier "));
+
+ if (!ao->neg_ifaceid ||
+ cilen != CILEN_IFACEID) { /* Check CI length */
+ orc = CONFREJ; /* Reject CI */
+ break;
+ }
+
+ /*
+ * If he has no interface identifier, or if we both have same
+ * identifier then NAK it with new idea.
+ * In particular, if we don't know his identifier, but he does,
+ * then accept it.
+ */
+ eui64_get(ifaceid, p);
+ IPV6CPDEBUG(("(%s)", llv6_ntoa(ifaceid)));
+ if (eui64_iszero(ifaceid) && eui64_iszero(go->ourid)) {
+ orc = CONFREJ; /* Reject CI */
+ break;
+ }
+ if (!eui64_iszero(wo->hisid) &&
+ !eui64_equals(ifaceid, wo->hisid) &&
+ eui64_iszero(go->hisid)) {
+
+ orc = CONFNAK;
+ ifaceid = wo->hisid;
+ go->hisid = ifaceid;
+ DECPTR(sizeof(ifaceid), p);
+ eui64_put(ifaceid, p);
+ } else
+ if (eui64_iszero(ifaceid) || eui64_equals(ifaceid, go->ourid)) {
+ orc = CONFNAK;
+ if (eui64_iszero(go->hisid)) /* first time, try option */
+ ifaceid = wo->hisid;
+ while (eui64_iszero(ifaceid) ||
+ eui64_equals(ifaceid, go->ourid)) /* bad luck */
+ eui64_magic(ifaceid);
+ go->hisid = ifaceid;
+ DECPTR(sizeof(ifaceid), p);
+ eui64_put(ifaceid, p);
+ }
+
+ ho->neg_ifaceid = 1;
+ ho->hisid = ifaceid;
+ break;
+
+#ifdef IPV6CP_COMP
+ case CI_COMPRESSTYPE:
+ IPV6CPDEBUG(("ipv6cp: received COMPRESSTYPE "));
+ if (!ao->neg_vj ||
+ (cilen != CILEN_COMPRESS)) {
+ orc = CONFREJ;
+ break;
+ }
+ GETSHORT(cishort, p);
+ IPV6CPDEBUG(("(%d)", cishort));
+
+ if (!(cishort == IPV6CP_COMP)) {
+ orc = CONFREJ;
+ break;
+ }
+
+ ho->neg_vj = 1;
+ ho->vj_protocol = cishort;
+ break;
+#endif /* IPV6CP_COMP */
+
+ default:
+ orc = CONFREJ;
+ break;
+ }
+
+endswitch:
+ IPV6CPDEBUG((" (%s)\n", CODENAME(orc)));
+
+ 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? */
+ 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)
+ MEMCPY(ucp, cip, cilen); /* Move it */
+
+ /* Update output pointer */
+ INCPTR(cilen, ucp);
+ }
+
+ /*
+ * If we aren't rejecting this packet, and we want to negotiate
+ * their identifier and they didn't send their identifier, then we
+ * send a NAK with a CI_IFACEID option appended. We assume the
+ * input buffer is long enough that we can append the extra
+ * option safely.
+ */
+ if (rc != CONFREJ && !ho->neg_ifaceid &&
+ wo->req_ifaceid && !reject_if_disagree) {
+ if (rc == CONFACK) {
+ rc = CONFNAK;
+ ucp = inp; /* reset pointer */
+ wo->req_ifaceid = 0; /* don't ask again */
+ }
+ PUTCHAR(CI_IFACEID, ucp);
+ PUTCHAR(CILEN_IFACEID, ucp);
+ eui64_put(wo->hisid, ucp);
+ }
+
+ *len = ucp - inp; /* Compute output length */
+ IPV6CPDEBUG(("ipv6cp: returning Configure-%s", CODENAME(rc)));
+ return (rc); /* Return final code */
+}
+
+#if PPP_OPTIONS
+/*
+ * ipv6_check_options - check that any IP-related options are OK,
+ * and assign appropriate defaults.
+ */
+static void ipv6_check_options() {
+ ipv6cp_options *wo = &ipv6cp_wantoptions[0];
+
+ if (!ipv6cp_protent.enabled_flag)
+ return;
+
+ /*
+ * Persistent link-local id is only used when user has not explicitly
+ * configure/hard-code the id
+ */
+ if ((wo->use_persistent) && (!wo->opt_local) && (!wo->opt_remote)) {
+
+ /*
+ * On systems where there are no Ethernet interfaces used, there
+ * may be other ways to obtain a persistent id. Right now, it
+ * will fall back to using magic [see eui64_magic] below when
+ * an EUI-48 from MAC address can't be obtained. Other possibilities
+ * include obtaining EEPROM serial numbers, or some other unique
+ * yet persistent number. On Sparc platforms, this is possible,
+ * but too bad there's no standards yet for x86 machines.
+ */
+ if (ether_to_eui64(&wo->ourid)) {
+ wo->opt_local = 1;
+ }
+ }
+
+ if (!wo->opt_local) { /* init interface identifier */
+ if (wo->use_ip && eui64_iszero(wo->ourid)) {
+ eui64_setlo32(wo->ourid, lwip_ntohl(ipcp_wantoptions[0].ouraddr));
+ if (!eui64_iszero(wo->ourid))
+ wo->opt_local = 1;
+ }
+
+ while (eui64_iszero(wo->ourid))
+ eui64_magic(wo->ourid);
+ }
+
+ if (!wo->opt_remote) {
+ if (wo->use_ip && eui64_iszero(wo->hisid)) {
+ eui64_setlo32(wo->hisid, lwip_ntohl(ipcp_wantoptions[0].hisaddr));
+ if (!eui64_iszero(wo->hisid))
+ wo->opt_remote = 1;
+ }
+ }
+
+ if (demand && (eui64_iszero(wo->ourid) || eui64_iszero(wo->hisid))) {
+ option_error("local/remote LL address required for demand-dialling\n");
+ exit(1);
+ }
+}
+#endif /* PPP_OPTIONS */
+
+#if DEMAND_SUPPORT
+/*
+ * ipv6_demand_conf - configure the interface as though
+ * IPV6CP were up, for use with dial-on-demand.
+ */
+static int ipv6_demand_conf(int u) {
+ ipv6cp_options *wo = &ipv6cp_wantoptions[u];
+
+ if (!sif6up(u))
+ return 0;
+
+ if (!sif6addr(u, wo->ourid, wo->hisid))
+ return 0;
+
+ if (!sifnpmode(u, PPP_IPV6, NPMODE_QUEUE))
+ return 0;
+
+ ppp_notice("ipv6_demand_conf");
+ ppp_notice("local LL address %s", llv6_ntoa(wo->ourid));
+ ppp_notice("remote LL address %s", llv6_ntoa(wo->hisid));
+
+ return 1;
+}
+#endif /* DEMAND_SUPPORT */
+
+
+/*
+ * ipv6cp_up - IPV6CP has come UP.
+ *
+ * Configure the IPv6 network interface appropriately and bring it up.
+ */
+static void ipv6cp_up(fsm *f) {
+ ppp_pcb *pcb = f->pcb;
+ ipv6cp_options *wo = &pcb->ipv6cp_wantoptions;
+ ipv6cp_options *ho = &pcb->ipv6cp_hisoptions;
+ ipv6cp_options *go = &pcb->ipv6cp_gotoptions;
+
+ IPV6CPDEBUG(("ipv6cp: up"));
+
+ /*
+ * We must have a non-zero LL address for both ends of the link.
+ */
+ if (!ho->neg_ifaceid)
+ ho->hisid = wo->hisid;
+
+#if 0 /* UNUSED */
+ if(!no_ifaceid_neg) {
+#endif /* UNUSED */
+ if (eui64_iszero(ho->hisid)) {
+ ppp_error("Could not determine remote LL address");
+ ipv6cp_close(f->pcb, "Could not determine remote LL address");
+ return;
+ }
+ if (eui64_iszero(go->ourid)) {
+ ppp_error("Could not determine local LL address");
+ ipv6cp_close(f->pcb, "Could not determine local LL address");
+ return;
+ }
+ if (eui64_equals(go->ourid, ho->hisid)) {
+ ppp_error("local and remote LL addresses are equal");
+ ipv6cp_close(f->pcb, "local and remote LL addresses are equal");
+ return;
+ }
+#if 0 /* UNUSED */
+ }
+#endif /* UNUSED */
+#if 0 /* UNUSED */
+ script_setenv("LLLOCAL", llv6_ntoa(go->ourid), 0);
+ script_setenv("LLREMOTE", llv6_ntoa(ho->hisid), 0);
+#endif /* UNUSED */
+
+#ifdef IPV6CP_COMP
+ /* set tcp compression */
+ sif6comp(f->unit, ho->neg_vj);
+#endif
+
+#if DEMAND_SUPPORT
+ /*
+ * If we are doing dial-on-demand, the interface is already
+ * configured, so we put out any saved-up packets, then set the
+ * interface to pass IPv6 packets.
+ */
+ if (demand) {
+ if (! eui64_equals(go->ourid, wo->ourid) ||
+ ! eui64_equals(ho->hisid, wo->hisid)) {
+ if (! eui64_equals(go->ourid, wo->ourid))
+ warn("Local LL address changed to %s",
+ llv6_ntoa(go->ourid));
+ if (! eui64_equals(ho->hisid, wo->hisid))
+ warn("Remote LL address changed to %s",
+ llv6_ntoa(ho->hisid));
+ ipv6cp_clear_addrs(f->pcb, go->ourid, ho->hisid);
+
+ /* Set the interface to the new addresses */
+ if (!sif6addr(f->pcb, go->ourid, ho->hisid)) {
+ if (debug)
+ warn("sif6addr failed");
+ ipv6cp_close(f->unit, "Interface configuration failed");
+ return;
+ }
+
+ }
+ demand_rexmit(PPP_IPV6);
+ sifnpmode(f->unit, PPP_IPV6, NPMODE_PASS);
+
+ } else
+#endif /* DEMAND_SUPPORT */
+ {
+ /*
+ * Set LL addresses
+ */
+ if (!sif6addr(f->pcb, go->ourid, ho->hisid)) {
+ PPPDEBUG(LOG_DEBUG, ("sif6addr failed"));
+ ipv6cp_close(f->pcb, "Interface configuration failed");
+ return;
+ }
+
+ /* bring the interface up for IPv6 */
+ if (!sif6up(f->pcb)) {
+ PPPDEBUG(LOG_DEBUG, ("sif6up failed (IPV6)"));
+ ipv6cp_close(f->pcb, "Interface configuration failed");
+ return;
+ }
+#if DEMAND_SUPPORT
+ sifnpmode(f->pcb, PPP_IPV6, NPMODE_PASS);
+#endif /* DEMAND_SUPPORT */
+
+ ppp_notice("local LL address %s", llv6_ntoa(go->ourid));
+ ppp_notice("remote LL address %s", llv6_ntoa(ho->hisid));
+ }
+
+ np_up(f->pcb, PPP_IPV6);
+ pcb->ipv6cp_is_up = 1;
+
+#if 0 /* UNUSED */
+ /*
+ * Execute the ipv6-up script, like this:
+ * /etc/ppp/ipv6-up interface tty speed local-LL remote-LL
+ */
+ if (ipv6cp_script_state == s_down && ipv6cp_script_pid == 0) {
+ ipv6cp_script_state = s_up;
+ ipv6cp_script(_PATH_IPV6UP);
+ }
+#endif /* UNUSED */
+}
+
+
+/*
+ * ipv6cp_down - IPV6CP has gone DOWN.
+ *
+ * Take the IPv6 network interface down, clear its addresses
+ * and delete routes through it.
+ */
+static void ipv6cp_down(fsm *f) {
+ ppp_pcb *pcb = f->pcb;
+ ipv6cp_options *go = &pcb->ipv6cp_gotoptions;
+ ipv6cp_options *ho = &pcb->ipv6cp_hisoptions;
+
+ IPV6CPDEBUG(("ipv6cp: down"));
+#if PPP_STATS_SUPPORT
+ update_link_stats(f->unit);
+#endif /* PPP_STATS_SUPPORT */
+ if (pcb->ipv6cp_is_up) {
+ pcb->ipv6cp_is_up = 0;
+ np_down(f->pcb, PPP_IPV6);
+ }
+#ifdef IPV6CP_COMP
+ sif6comp(f->unit, 0);
+#endif
+
+#if DEMAND_SUPPORT
+ /*
+ * If we are doing dial-on-demand, set the interface
+ * to queue up outgoing packets (for now).
+ */
+ if (demand) {
+ sifnpmode(f->pcb, PPP_IPV6, NPMODE_QUEUE);
+ } else
+#endif /* DEMAND_SUPPORT */
+ {
+#if DEMAND_SUPPORT
+ sifnpmode(f->pcb, PPP_IPV6, NPMODE_DROP);
+#endif /* DEMAND_SUPPORT */
+ ipv6cp_clear_addrs(f->pcb,
+ go->ourid,
+ ho->hisid);
+ sif6down(f->pcb);
+ }
+
+#if 0 /* UNUSED */
+ /* Execute the ipv6-down script */
+ if (ipv6cp_script_state == s_up && ipv6cp_script_pid == 0) {
+ ipv6cp_script_state = s_down;
+ ipv6cp_script(_PATH_IPV6DOWN);
+ }
+#endif /* UNUSED */
+}
+
+
+/*
+ * ipv6cp_clear_addrs() - clear the interface addresses, routes,
+ * proxy neighbour discovery entries, etc.
+ */
+static void ipv6cp_clear_addrs(ppp_pcb *pcb, eui64_t ourid, eui64_t hisid) {
+ cif6addr(pcb, ourid, hisid);
+}
+
+
+/*
+ * ipv6cp_finished - possibly shut down the lower layers.
+ */
+static void ipv6cp_finished(fsm *f) {
+ np_finished(f->pcb, PPP_IPV6);
+}
+
+
+#if 0 /* UNUSED */
+/*
+ * ipv6cp_script_done - called when the ipv6-up or ipv6-down script
+ * has finished.
+ */
+static void
+ipv6cp_script_done(arg)
+ void *arg;
+{
+ ipv6cp_script_pid = 0;
+ switch (ipv6cp_script_state) {
+ case s_up:
+ if (ipv6cp_fsm[0].state != PPP_FSM_OPENED) {
+ ipv6cp_script_state = s_down;
+ ipv6cp_script(_PATH_IPV6DOWN);
+ }
+ break;
+ case s_down:
+ if (ipv6cp_fsm[0].state == PPP_FSM_OPENED) {
+ ipv6cp_script_state = s_up;
+ ipv6cp_script(_PATH_IPV6UP);
+ }
+ break;
+ }
+}
+
+
+/*
+ * ipv6cp_script - Execute a script with arguments
+ * interface-name tty-name speed local-LL remote-LL.
+ */
+static void
+ipv6cp_script(script)
+ char *script;
+{
+ char strspeed[32], strlocal[32], strremote[32];
+ char *argv[8];
+
+ sprintf(strspeed, "%d", baud_rate);
+ strcpy(strlocal, llv6_ntoa(ipv6cp_gotoptions[0].ourid));
+ strcpy(strremote, llv6_ntoa(ipv6cp_hisoptions[0].hisid));
+
+ argv[0] = script;
+ argv[1] = ifname;
+ argv[2] = devnam;
+ argv[3] = strspeed;
+ argv[4] = strlocal;
+ argv[5] = strremote;
+ argv[6] = ipparam;
+ argv[7] = NULL;
+
+ ipv6cp_script_pid = run_program(script, argv, 0, ipv6cp_script_done,
+ NULL, 0);
+}
+#endif /* UNUSED */
+
+#if PRINTPKT_SUPPORT
+/*
+ * ipv6cp_printpkt - print the contents of an IPV6CP packet.
+ */
+static const char* const ipv6cp_codenames[] = {
+ "ConfReq", "ConfAck", "ConfNak", "ConfRej",
+ "TermReq", "TermAck", "CodeRej"
+};
+
+static int ipv6cp_printpkt(const u_char *p, int plen,
+ void (*printer)(void *, const char *, ...), void *arg) {
+ int code, id, len, olen;
+ const u_char *pstart, *optend;
+#ifdef IPV6CP_COMP
+ u_short cishort;
+#endif /* IPV6CP_COMP */
+ eui64_t ifaceid;
+
+ 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 <= (int)LWIP_ARRAYSIZE(ipv6cp_codenames))
+ printer(arg, " %s", ipv6cp_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) {
+#ifdef IPV6CP_COMP
+ case CI_COMPRESSTYPE:
+ if (olen >= CILEN_COMPRESS) {
+ p += 2;
+ GETSHORT(cishort, p);
+ printer(arg, "compress ");
+ printer(arg, "0x%x", cishort);
+ }
+ break;
+#endif /* IPV6CP_COMP */
+ case CI_IFACEID:
+ if (olen == CILEN_IFACEID) {
+ p += 2;
+ eui64_get(ifaceid, p);
+ printer(arg, "addr %s", llv6_ntoa(ifaceid));
+ }
+ break;
+ default:
+ 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, " ");
+ ppp_print_string(p, len, printer, arg);
+ p += len;
+ len = 0;
+ }
+ break;
+ default:
+ break;
+ }
+
+ /* print the rest of the bytes in the packet */
+ for (; len > 0; --len) {
+ GETCHAR(code, p);
+ printer(arg, " %.2x", code);
+ }
+
+ return p - pstart;
+}
+#endif /* PRINTPKT_SUPPORT */
+
+#if DEMAND_SUPPORT
+/*
+ * ipv6_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 IP6_HDRLEN 40 /* bytes */
+#define IP6_NHDR_FRAG 44 /* fragment IPv6 header */
+#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 get_ip6nh(x) (((unsigned char *)(x))[6])
+#define get_tcpoff(x) (((unsigned char *)(x))[12] >> 4)
+#define get_tcpflags(x) (((unsigned char *)(x))[13])
+
+static int ipv6_active_pkt(u_char *pkt, int len) {
+ u_char *tcp;
+
+ len -= PPP_HDRLEN;
+ pkt += PPP_HDRLEN;
+ if (len < IP6_HDRLEN)
+ return 0;
+ if (get_ip6nh(pkt) == IP6_NHDR_FRAG)
+ return 0;
+ if (get_ip6nh(pkt) != IPPROTO_TCP)
+ return 1;
+ if (len < IP6_HDRLEN + TCP_HDRLEN)
+ return 0;
+ tcp = pkt + IP6_HDRLEN;
+ if ((get_tcpflags(tcp) & TH_FIN) != 0 && len == IP6_HDRLEN + get_tcpoff(tcp) * 4)
+ return 0;
+ return 1;
+}
+#endif /* DEMAND_SUPPORT */
+
+#endif /* PPP_SUPPORT && PPP_IPV6_SUPPORT */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/lcp.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/lcp.c
new file mode 100644
index 0000000..90ed183
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/lcp.c
@@ -0,0 +1,2790 @@
+/*
+ * lcp.c - PPP Link Control Protocol.
+ *
+ * Copyright (c) 1984-2000 Carnegie Mellon University. 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 "Carnegie Mellon University" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission. For permission or any legal
+ * details, please contact
+ * Office of Technology Transfer
+ * Carnegie Mellon University
+ * 5000 Forbes Avenue
+ * Pittsburgh, PA 15213-3890
+ * (412) 268-4387, fax: (412) 268-7395
+ * tech-transfer@andrew.cmu.edu
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by Computing Services
+ * at Carnegie Mellon University (http://www.cmu.edu/computing/)."
+ *
+ * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
+ * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+/*
+ * @todo:
+ */
+
+#if 0 /* UNUSED */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#endif /* UNUSED */
+
+#include "netif/ppp/ppp_impl.h"
+
+#include "netif/ppp/fsm.h"
+#include "netif/ppp/lcp.h"
+#if CHAP_SUPPORT
+#include "netif/ppp/chap-new.h"
+#endif /* CHAP_SUPPORT */
+#include "netif/ppp/magic.h"
+
+/*
+ * When the link comes up we want to be able to wait for a short while,
+ * or until seeing some input from the peer, before starting to send
+ * configure-requests. We do this by delaying the fsm_lowerup call.
+ */
+/* steal a bit in fsm flags word */
+#define DELAYED_UP 0x80
+
+static void lcp_delayed_up(void *arg);
+
+/*
+ * LCP-related command-line options.
+ */
+#if 0 /* UNUSED */
+int lcp_echo_interval = 0; /* Interval between LCP echo-requests */
+int lcp_echo_fails = 0; /* Tolerance to unanswered echo-requests */
+#endif /* UNUSED */
+
+#if 0 /* UNUSED */
+/* options */
+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 */
+#endif /* UNUSED */
+
+#if 0 /* UNUSED */
+#if PPP_LCP_ADAPTIVE
+bool lcp_echo_adaptive = 0; /* request echo only if the link was idle */
+#endif
+bool lax_recv = 0; /* accept control chars in asyncmap */
+bool noendpoint = 0; /* don't send/accept endpoint discriminator */
+#endif /* UNUSED */
+
+#if PPP_OPTIONS
+static int noopt (char **);
+#endif /* PPP_OPTIONS */
+
+#ifdef HAVE_MULTILINK
+static int setendpoint (char **);
+static void printendpoint (option_t *, void (*)(void *, char *, ...),
+ void *);
+#endif /* HAVE_MULTILINK */
+
+#if PPP_OPTIONS
+static option_t lcp_option_list[] = {
+ /* LCP options */
+ { "-all", o_special_noarg, (void *)noopt,
+ "Don't request/allow any LCP options" },
+
+ { "noaccomp", o_bool, &lcp_wantoptions[0].neg_accompression,
+ "Disable address/control compression",
+ OPT_A2CLR, &lcp_allowoptions[0].neg_accompression },
+ { "-ac", o_bool, &lcp_wantoptions[0].neg_accompression,
+ "Disable address/control compression",
+ OPT_ALIAS | OPT_A2CLR, &lcp_allowoptions[0].neg_accompression },
+
+ { "asyncmap", o_uint32, &lcp_wantoptions[0].asyncmap,
+ "Set asyncmap (for received packets)",
+ OPT_OR, &lcp_wantoptions[0].neg_asyncmap },
+ { "-as", o_uint32, &lcp_wantoptions[0].asyncmap,
+ "Set asyncmap (for received packets)",
+ OPT_ALIAS | OPT_OR, &lcp_wantoptions[0].neg_asyncmap },
+ { "default-asyncmap", o_uint32, &lcp_wantoptions[0].asyncmap,
+ "Disable asyncmap negotiation",
+ OPT_OR | OPT_NOARG | OPT_VAL(~0U) | OPT_A2CLR,
+ &lcp_allowoptions[0].neg_asyncmap },
+ { "-am", o_uint32, &lcp_wantoptions[0].asyncmap,
+ "Disable asyncmap negotiation",
+ OPT_ALIAS | OPT_OR | OPT_NOARG | OPT_VAL(~0U) | OPT_A2CLR,
+ &lcp_allowoptions[0].neg_asyncmap },
+
+ { "nomagic", o_bool, &lcp_wantoptions[0].neg_magicnumber,
+ "Disable magic number negotiation (looped-back line detection)",
+ OPT_A2CLR, &lcp_allowoptions[0].neg_magicnumber },
+ { "-mn", o_bool, &lcp_wantoptions[0].neg_magicnumber,
+ "Disable magic number negotiation (looped-back line detection)",
+ OPT_ALIAS | OPT_A2CLR, &lcp_allowoptions[0].neg_magicnumber },
+
+ { "mru", o_int, &lcp_wantoptions[0].mru,
+ "Set MRU (maximum received packet size) for negotiation",
+ OPT_PRIO, &lcp_wantoptions[0].neg_mru },
+ { "default-mru", o_bool, &lcp_wantoptions[0].neg_mru,
+ "Disable MRU negotiation (use default 1500)",
+ OPT_PRIOSUB | OPT_A2CLR, &lcp_allowoptions[0].neg_mru },
+ { "-mru", o_bool, &lcp_wantoptions[0].neg_mru,
+ "Disable MRU negotiation (use default 1500)",
+ OPT_ALIAS | OPT_PRIOSUB | OPT_A2CLR, &lcp_allowoptions[0].neg_mru },
+
+ { "mtu", o_int, &lcp_allowoptions[0].mru,
+ "Set our MTU", OPT_LIMITS, NULL, MAXMRU, MINMRU },
+
+ { "nopcomp", o_bool, &lcp_wantoptions[0].neg_pcompression,
+ "Disable protocol field compression",
+ OPT_A2CLR, &lcp_allowoptions[0].neg_pcompression },
+ { "-pc", o_bool, &lcp_wantoptions[0].neg_pcompression,
+ "Disable protocol field compression",
+ OPT_ALIAS | OPT_A2CLR, &lcp_allowoptions[0].neg_pcompression },
+
+ { "passive", o_bool, &lcp_wantoptions[0].passive,
+ "Set passive mode", 1 },
+ { "-p", o_bool, &lcp_wantoptions[0].passive,
+ "Set passive mode", OPT_ALIAS | 1 },
+
+ { "silent", o_bool, &lcp_wantoptions[0].silent,
+ "Set silent mode", 1 },
+
+ { "lcp-echo-failure", o_int, &lcp_echo_fails,
+ "Set number of consecutive echo failures to indicate link failure",
+ OPT_PRIO },
+ { "lcp-echo-interval", o_int, &lcp_echo_interval,
+ "Set time in seconds between LCP echo requests", OPT_PRIO },
+#if PPP_LCP_ADAPTIVE
+ { "lcp-echo-adaptive", o_bool, &lcp_echo_adaptive,
+ "Suppress LCP echo requests if traffic was received", 1 },
+#endif
+ { "lcp-restart", o_int, &lcp_fsm[0].timeouttime,
+ "Set time in seconds between LCP retransmissions", OPT_PRIO },
+ { "lcp-max-terminate", o_int, &lcp_fsm[0].maxtermtransmits,
+ "Set maximum number of LCP terminate-request transmissions", OPT_PRIO },
+ { "lcp-max-configure", o_int, &lcp_fsm[0].maxconfreqtransmits,
+ "Set maximum number of LCP configure-request transmissions", OPT_PRIO },
+ { "lcp-max-failure", o_int, &lcp_fsm[0].maxnakloops,
+ "Set limit on number of LCP configure-naks", OPT_PRIO },
+
+ { "receive-all", o_bool, &lax_recv,
+ "Accept all received control characters", 1 },
+
+#ifdef HAVE_MULTILINK
+ { "mrru", o_int, &lcp_wantoptions[0].mrru,
+ "Maximum received packet size for multilink bundle",
+ OPT_PRIO, &lcp_wantoptions[0].neg_mrru },
+
+ { "mpshortseq", o_bool, &lcp_wantoptions[0].neg_ssnhf,
+ "Use short sequence numbers in multilink headers",
+ OPT_PRIO | 1, &lcp_allowoptions[0].neg_ssnhf },
+ { "nompshortseq", o_bool, &lcp_wantoptions[0].neg_ssnhf,
+ "Don't use short sequence numbers in multilink headers",
+ OPT_PRIOSUB | OPT_A2CLR, &lcp_allowoptions[0].neg_ssnhf },
+
+ { "endpoint", o_special, (void *) setendpoint,
+ "Endpoint discriminator for multilink",
+ OPT_PRIO | OPT_A2PRINTER, (void *) printendpoint },
+#endif /* HAVE_MULTILINK */
+
+ { "noendpoint", o_bool, &noendpoint,
+ "Don't send or accept multilink endpoint discriminator", 1 },
+
+ {NULL}
+};
+#endif /* PPP_OPTIONS */
+
+/*
+ * Callbacks for fsm code. (CI = Configuration Information)
+ */
+static void lcp_resetci(fsm *f); /* Reset our CI */
+static int lcp_cilen(fsm *f); /* Return length of our CI */
+static void lcp_addci(fsm *f, u_char *ucp, int *lenp); /* Add our CI to pkt */
+static int lcp_ackci(fsm *f, u_char *p, int len); /* Peer ack'd our CI */
+static int lcp_nakci(fsm *f, u_char *p, int len, int treat_as_reject); /* Peer nak'd our CI */
+static int lcp_rejci(fsm *f, u_char *p, int len); /* Peer rej'd our CI */
+static int lcp_reqci(fsm *f, u_char *inp, int *lenp, int reject_if_disagree); /* Rcv peer CI */
+static void lcp_up(fsm *f); /* We're UP */
+static void lcp_down(fsm *f); /* 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 *f, int code, int id, u_char *inp, int len);
+static void lcp_rprotrej(fsm *f, u_char *inp, int len);
+
+/*
+ * routines to send LCP echos to peer
+ */
+
+static void lcp_echo_lowerup(ppp_pcb *pcb);
+static void lcp_echo_lowerdown(ppp_pcb *pcb);
+static void LcpEchoTimeout(void *arg);
+static void lcp_received_echo_reply(fsm *f, int id, u_char *inp, int len);
+static void LcpSendEchoRequest(fsm *f);
+static void LcpLinkFailure(fsm *f);
+static void LcpEchoCheck(fsm *f);
+
+static const 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 OPENED state */
+ lcp_down, /* Called when fsm leaves 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_init(ppp_pcb *pcb);
+static void lcp_input(ppp_pcb *pcb, u_char *p, int len);
+static void lcp_protrej(ppp_pcb *pcb);
+#if PRINTPKT_SUPPORT
+static int lcp_printpkt(const u_char *p, int plen,
+ void (*printer) (void *, const char *, ...), void *arg);
+#endif /* PRINTPKT_SUPPORT */
+
+const struct protent lcp_protent = {
+ PPP_LCP,
+ lcp_init,
+ lcp_input,
+ lcp_protrej,
+ lcp_lowerup,
+ lcp_lowerdown,
+ lcp_open,
+ lcp_close,
+#if PRINTPKT_SUPPORT
+ lcp_printpkt,
+#endif /* PRINTPKT_SUPPORT */
+#if PPP_DATAINPUT
+ NULL,
+#endif /* PPP_DATAINPUT */
+#if PRINTPKT_SUPPORT
+ "LCP",
+ NULL,
+#endif /* PRINTPKT_SUPPORT */
+#if PPP_OPTIONS
+ lcp_option_list,
+ NULL,
+#endif /* PPP_OPTIONS */
+#if DEMAND_SUPPORT
+ NULL,
+ NULL
+#endif /* DEMAND_SUPPORT */
+};
+
+/*
+ * Length of each type of configuration option (in octets)
+ */
+#define CILEN_VOID 2
+#define CILEN_CHAR 3
+#define CILEN_SHORT 4 /* CILEN_VOID + 2 */
+#if CHAP_SUPPORT
+#define CILEN_CHAP 5 /* CILEN_VOID + 2 + 1 */
+#endif /* CHAP_SUPPORT */
+#define CILEN_LONG 6 /* CILEN_VOID + 4 */
+#if LQR_SUPPORT
+#define CILEN_LQR 8 /* CILEN_VOID + 2 + 4 */
+#endif /* LQR_SUPPORT */
+#define CILEN_CBCP 3
+
+#define CODENAME(x) ((x) == CONFACK ? "ACK" : \
+ (x) == CONFNAK ? "NAK" : "REJ")
+
+#if PPP_OPTIONS
+/*
+ * noopt - Disable all options (why?).
+ */
+static int
+noopt(argv)
+ char **argv;
+{
+ BZERO((char *) &lcp_wantoptions[0], sizeof (struct lcp_options));
+ BZERO((char *) &lcp_allowoptions[0], sizeof (struct lcp_options));
+
+ return (1);
+}
+#endif /* PPP_OPTIONS */
+
+#ifdef HAVE_MULTILINK
+static int
+setendpoint(argv)
+ char **argv;
+{
+ if (str_to_epdisc(&lcp_wantoptions[0].endpoint, *argv)) {
+ lcp_wantoptions[0].neg_endpoint = 1;
+ return 1;
+ }
+ option_error("Can't parse '%s' as an endpoint discriminator", *argv);
+ return 0;
+}
+
+static void
+printendpoint(opt, printer, arg)
+ option_t *opt;
+ void (*printer) (void *, char *, ...);
+ void *arg;
+{
+ printer(arg, "%s", epdisc_to_str(&lcp_wantoptions[0].endpoint));
+}
+#endif /* HAVE_MULTILINK */
+
+/*
+ * lcp_init - Initialize LCP.
+ */
+static void lcp_init(ppp_pcb *pcb) {
+ fsm *f = &pcb->lcp_fsm;
+ lcp_options *wo = &pcb->lcp_wantoptions;
+ lcp_options *ao = &pcb->lcp_allowoptions;
+
+ f->pcb = pcb;
+ f->protocol = PPP_LCP;
+ f->callbacks = &lcp_callbacks;
+
+ fsm_init(f);
+
+ BZERO(wo, sizeof(*wo));
+ wo->neg_mru = 1;
+ wo->mru = PPP_DEFMRU;
+ wo->neg_asyncmap = 1;
+ wo->neg_magicnumber = 1;
+ wo->neg_pcompression = 1;
+ wo->neg_accompression = 1;
+
+ BZERO(ao, sizeof(*ao));
+ ao->neg_mru = 1;
+ ao->mru = PPP_MAXMRU;
+ ao->neg_asyncmap = 1;
+#if CHAP_SUPPORT
+ ao->neg_chap = 1;
+ ao->chap_mdtype = CHAP_MDTYPE_SUPPORTED;
+#endif /* CHAP_SUPPORT */
+#if PAP_SUPPORT
+ ao->neg_upap = 1;
+#endif /* PAP_SUPPORT */
+#if EAP_SUPPORT
+ ao->neg_eap = 1;
+#endif /* EAP_SUPPORT */
+ ao->neg_magicnumber = 1;
+ ao->neg_pcompression = 1;
+ ao->neg_accompression = 1;
+ ao->neg_endpoint = 1;
+}
+
+
+/*
+ * lcp_open - LCP is allowed to come up.
+ */
+void lcp_open(ppp_pcb *pcb) {
+ fsm *f = &pcb->lcp_fsm;
+ lcp_options *wo = &pcb->lcp_wantoptions;
+
+ f->flags &= ~(OPT_PASSIVE | OPT_SILENT);
+ if (wo->passive)
+ f->flags |= OPT_PASSIVE;
+ if (wo->silent)
+ f->flags |= OPT_SILENT;
+ fsm_open(f);
+}
+
+
+/*
+ * lcp_close - Take LCP down.
+ */
+void lcp_close(ppp_pcb *pcb, const char *reason) {
+ fsm *f = &pcb->lcp_fsm;
+ int oldstate;
+
+ if (pcb->phase != PPP_PHASE_DEAD
+#ifdef HAVE_MULTILINK
+ && pcb->phase != PPP_PHASE_MASTER
+#endif /* HAVE_MULTILINK */
+ )
+ new_phase(pcb, PPP_PHASE_TERMINATE);
+
+ if (f->flags & DELAYED_UP) {
+ UNTIMEOUT(lcp_delayed_up, f);
+ f->state = PPP_FSM_STOPPED;
+ }
+ oldstate = f->state;
+
+ fsm_close(f, reason);
+ if (oldstate == PPP_FSM_STOPPED && (f->flags & (OPT_PASSIVE|OPT_SILENT|DELAYED_UP))) {
+ /*
+ * This action is not strictly according to the FSM in RFC1548,
+ * but it does mean that the program terminates if you do a
+ * lcp_close() when a connection hasn't been established
+ * because we are in passive/silent mode or because we have
+ * delayed the fsm_lowerup() call and it hasn't happened yet.
+ */
+ f->flags &= ~DELAYED_UP;
+ lcp_finished(f);
+ }
+}
+
+
+/*
+ * lcp_lowerup - The lower layer is up.
+ */
+void lcp_lowerup(ppp_pcb *pcb) {
+ lcp_options *wo = &pcb->lcp_wantoptions;
+ fsm *f = &pcb->lcp_fsm;
+ /*
+ * 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.
+ */
+ if (ppp_send_config(pcb, PPP_MRU, 0xffffffff, 0, 0) < 0
+ || ppp_recv_config(pcb, PPP_MRU, (pcb->settings.lax_recv? 0: 0xffffffff),
+ wo->neg_pcompression, wo->neg_accompression) < 0)
+ return;
+ pcb->peer_mru = PPP_MRU;
+
+ if (pcb->settings.listen_time != 0) {
+ f->flags |= DELAYED_UP;
+ TIMEOUTMS(lcp_delayed_up, f, pcb->settings.listen_time);
+ } else
+ fsm_lowerup(f);
+}
+
+
+/*
+ * lcp_lowerdown - The lower layer is down.
+ */
+void lcp_lowerdown(ppp_pcb *pcb) {
+ fsm *f = &pcb->lcp_fsm;
+
+ if (f->flags & DELAYED_UP) {
+ f->flags &= ~DELAYED_UP;
+ UNTIMEOUT(lcp_delayed_up, f);
+ } else
+ fsm_lowerdown(f);
+}
+
+
+/*
+ * lcp_delayed_up - Bring the lower layer up now.
+ */
+static void lcp_delayed_up(void *arg) {
+ fsm *f = (fsm*)arg;
+
+ if (f->flags & DELAYED_UP) {
+ f->flags &= ~DELAYED_UP;
+ fsm_lowerup(f);
+ }
+}
+
+
+/*
+ * lcp_input - Input LCP packet.
+ */
+static void lcp_input(ppp_pcb *pcb, u_char *p, int len) {
+ fsm *f = &pcb->lcp_fsm;
+
+ if (f->flags & DELAYED_UP) {
+ f->flags &= ~DELAYED_UP;
+ UNTIMEOUT(lcp_delayed_up, f);
+ fsm_lowerup(f);
+ }
+ fsm_input(f, p, len);
+}
+
+/*
+ * lcp_extcode - Handle a LCP-specific code.
+ */
+static int lcp_extcode(fsm *f, int code, int id, u_char *inp, int len) {
+ ppp_pcb *pcb = f->pcb;
+ lcp_options *go = &pcb->lcp_gotoptions;
+ u_char *magp;
+
+ switch( code ){
+ case PROTREJ:
+ lcp_rprotrej(f, inp, len);
+ break;
+
+ case ECHOREQ:
+ if (f->state != PPP_FSM_OPENED)
+ break;
+ magp = inp;
+ PUTLONG(go->magicnumber, magp);
+ fsm_sdata(f, ECHOREP, id, inp, len);
+ break;
+
+ case ECHOREP:
+ lcp_received_echo_reply(f, id, inp, len);
+ break;
+
+ case DISCREQ:
+ case IDENTIF:
+ case TIMEREM:
+ 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;
+ const struct protent *protp;
+ u_short prot;
+#if PPP_PROTOCOLNAME
+ const char *pname;
+#endif /* PPP_PROTOCOLNAME */
+
+ if (len < 2) {
+ LCPDEBUG(("lcp_rprotrej: Rcvd short Protocol-Reject packet!"));
+ return;
+ }
+
+ GETSHORT(prot, inp);
+
+ /*
+ * Protocol-Reject packets received in any state other than the LCP
+ * OPENED state SHOULD be silently discarded.
+ */
+ if( f->state != PPP_FSM_OPENED ){
+ LCPDEBUG(("Protocol-Reject discarded: LCP in state %d", f->state));
+ return;
+ }
+
+#if PPP_PROTOCOLNAME
+ pname = protocol_name(prot);
+#endif /* PPP_PROTOCOLNAME */
+
+ /*
+ * Upcall the proper Protocol-Reject routine.
+ */
+ for (i = 0; (protp = protocols[i]) != NULL; ++i)
+ if (protp->protocol == prot) {
+#if PPP_PROTOCOLNAME
+ if (pname != NULL)
+ ppp_dbglog("Protocol-Reject for '%s' (0x%x) received", pname,
+ prot);
+ else
+#endif /* PPP_PROTOCOLNAME */
+ ppp_dbglog("Protocol-Reject for 0x%x received", prot);
+ (*protp->protrej)(f->pcb);
+ return;
+ }
+
+#if PPP_PROTOCOLNAME
+ if (pname != NULL)
+ ppp_warn("Protocol-Reject for unsupported protocol '%s' (0x%x)", pname,
+ prot);
+ else
+#endif /* #if PPP_PROTOCOLNAME */
+ ppp_warn("Protocol-Reject for unsupported protocol 0x%x", prot);
+}
+
+
+/*
+ * lcp_protrej - A Protocol-Reject was received.
+ */
+/*ARGSUSED*/
+static void lcp_protrej(ppp_pcb *pcb) {
+ /*
+ * Can't reject LCP!
+ */
+ ppp_error("Received Protocol-Reject for LCP!");
+ fsm_protreject(&pcb->lcp_fsm);
+}
+
+
+/*
+ * lcp_sprotrej - Send a Protocol-Reject for some protocol.
+ */
+void lcp_sprotrej(ppp_pcb *pcb, u_char *p, int len) {
+ fsm *f = &pcb->lcp_fsm;
+ /*
+ * Send back the protocol and the information field of the
+ * rejected packet. We only get here if LCP is in the OPENED state.
+ */
+#if 0
+ p += 2;
+ len -= 2;
+#endif
+
+ fsm_sdata(f, PROTREJ, ++f->id,
+ p, len);
+}
+
+
+/*
+ * lcp_resetci - Reset our CI.
+ */
+static void lcp_resetci(fsm *f) {
+ ppp_pcb *pcb = f->pcb;
+ lcp_options *wo = &pcb->lcp_wantoptions;
+ lcp_options *go = &pcb->lcp_gotoptions;
+ lcp_options *ao = &pcb->lcp_allowoptions;
+
+#if PPP_AUTH_SUPPORT
+
+ /* note: default value is true for allow options */
+ if (pcb->settings.user && pcb->settings.passwd) {
+#if PAP_SUPPORT
+ if (pcb->settings.refuse_pap) {
+ ao->neg_upap = 0;
+ }
+#endif /* PAP_SUPPORT */
+#if CHAP_SUPPORT
+ if (pcb->settings.refuse_chap) {
+ ao->chap_mdtype &= ~MDTYPE_MD5;
+ }
+#if MSCHAP_SUPPORT
+ if (pcb->settings.refuse_mschap) {
+ ao->chap_mdtype &= ~MDTYPE_MICROSOFT;
+ }
+ if (pcb->settings.refuse_mschap_v2) {
+ ao->chap_mdtype &= ~MDTYPE_MICROSOFT_V2;
+ }
+#endif /* MSCHAP_SUPPORT */
+ ao->neg_chap = (ao->chap_mdtype != MDTYPE_NONE);
+#endif /* CHAP_SUPPORT */
+#if EAP_SUPPORT
+ if (pcb->settings.refuse_eap) {
+ ao->neg_eap = 0;
+ }
+#endif /* EAP_SUPPORT */
+
+#if PPP_SERVER
+ /* note: default value is false for wanted options */
+ if (pcb->settings.auth_required) {
+#if PAP_SUPPORT
+ if (!pcb->settings.refuse_pap) {
+ wo->neg_upap = 1;
+ }
+#endif /* PAP_SUPPORT */
+#if CHAP_SUPPORT
+ if (!pcb->settings.refuse_chap) {
+ wo->chap_mdtype |= MDTYPE_MD5;
+ }
+#if MSCHAP_SUPPORT
+ if (!pcb->settings.refuse_mschap) {
+ wo->chap_mdtype |= MDTYPE_MICROSOFT;
+ }
+ if (!pcb->settings.refuse_mschap_v2) {
+ wo->chap_mdtype |= MDTYPE_MICROSOFT_V2;
+ }
+#endif /* MSCHAP_SUPPORT */
+ wo->neg_chap = (wo->chap_mdtype != MDTYPE_NONE);
+#endif /* CHAP_SUPPORT */
+#if EAP_SUPPORT
+ if (!pcb->settings.refuse_eap) {
+ wo->neg_eap = 1;
+ }
+#endif /* EAP_SUPPORT */
+ }
+#endif /* PPP_SERVER */
+
+ } else {
+#if PAP_SUPPORT
+ ao->neg_upap = 0;
+#endif /* PAP_SUPPORT */
+#if CHAP_SUPPORT
+ ao->neg_chap = 0;
+ ao->chap_mdtype = MDTYPE_NONE;
+#endif /* CHAP_SUPPORT */
+#if EAP_SUPPORT
+ ao->neg_eap = 0;
+#endif /* EAP_SUPPORT */
+ }
+
+ PPPDEBUG(LOG_DEBUG, ("ppp: auth protocols:"));
+#if PAP_SUPPORT
+ PPPDEBUG(LOG_DEBUG, (" PAP=%d", ao->neg_upap));
+#endif /* PAP_SUPPORT */
+#if CHAP_SUPPORT
+ PPPDEBUG(LOG_DEBUG, (" CHAP=%d CHAP_MD5=%d", ao->neg_chap, !!(ao->chap_mdtype&MDTYPE_MD5)));
+#if MSCHAP_SUPPORT
+ PPPDEBUG(LOG_DEBUG, (" CHAP_MS=%d CHAP_MS2=%d", !!(ao->chap_mdtype&MDTYPE_MICROSOFT), !!(ao->chap_mdtype&MDTYPE_MICROSOFT_V2)));
+#endif /* MSCHAP_SUPPORT */
+#endif /* CHAP_SUPPORT */
+#if EAP_SUPPORT
+ PPPDEBUG(LOG_DEBUG, (" EAP=%d", ao->neg_eap));
+#endif /* EAP_SUPPORT */
+ PPPDEBUG(LOG_DEBUG, ("\n"));
+
+#endif /* PPP_AUTH_SUPPORT */
+
+ wo->magicnumber = magic();
+ wo->numloops = 0;
+ *go = *wo;
+#ifdef HAVE_MULTILINK
+ if (!multilink) {
+ go->neg_mrru = 0;
+#endif /* HAVE_MULTILINK */
+ go->neg_ssnhf = 0;
+ go->neg_endpoint = 0;
+#ifdef HAVE_MULTILINK
+ }
+#endif /* HAVE_MULTILINK */
+ if (pcb->settings.noendpoint)
+ ao->neg_endpoint = 0;
+ pcb->peer_mru = PPP_MRU;
+#if 0 /* UNUSED */
+ auth_reset(pcb);
+#endif /* UNUSED */
+}
+
+
+/*
+ * lcp_cilen - Return length of our CI.
+ */
+static int lcp_cilen(fsm *f) {
+ ppp_pcb *pcb = f->pcb;
+ lcp_options *go = &pcb->lcp_gotoptions;
+
+#define LENCIVOID(neg) ((neg) ? CILEN_VOID : 0)
+#if CHAP_SUPPORT
+#define LENCICHAP(neg) ((neg) ? CILEN_CHAP : 0)
+#endif /* CHAP_SUPPORT */
+#define LENCISHORT(neg) ((neg) ? CILEN_SHORT : 0)
+#define LENCILONG(neg) ((neg) ? CILEN_LONG : 0)
+#if LQR_SUPPORT
+#define LENCILQR(neg) ((neg) ? CILEN_LQR: 0)
+#endif /* LQR_SUPPORT */
+#define LENCICBCP(neg) ((neg) ? CILEN_CBCP: 0)
+ /*
+ * NB: we only ask for one of CHAP, UPAP, or EAP, even if we will
+ * accept more than one. We prefer EAP first, then CHAP, then
+ * PAP.
+ */
+ return (LENCISHORT(go->neg_mru && go->mru != PPP_DEFMRU) +
+ LENCILONG(go->neg_asyncmap && go->asyncmap != 0xFFFFFFFF) +
+#if EAP_SUPPORT
+ LENCISHORT(go->neg_eap) +
+#endif /* EAP_SUPPORT */
+#if CHAP_SUPPORT /* cannot be improved, embedding a directive within macro arguments is not portable */
+#if EAP_SUPPORT
+ LENCICHAP(!go->neg_eap && go->neg_chap) +
+#endif /* EAP_SUPPORT */
+#if !EAP_SUPPORT
+ LENCICHAP(go->neg_chap) +
+#endif /* !EAP_SUPPORT */
+#endif /* CHAP_SUPPORT */
+#if PAP_SUPPORT /* cannot be improved, embedding a directive within macro arguments is not portable */
+#if EAP_SUPPORT && CHAP_SUPPORT
+ LENCISHORT(!go->neg_eap && !go->neg_chap && go->neg_upap) +
+#endif /* EAP_SUPPORT && CHAP_SUPPORT */
+#if EAP_SUPPORT && !CHAP_SUPPORT
+ LENCISHORT(!go->neg_eap && go->neg_upap) +
+#endif /* EAP_SUPPORT && !CHAP_SUPPORT */
+#if !EAP_SUPPORT && CHAP_SUPPORT
+ LENCISHORT(!go->neg_chap && go->neg_upap) +
+#endif /* !EAP_SUPPORT && CHAP_SUPPORT */
+#if !EAP_SUPPORT && !CHAP_SUPPORT
+ LENCISHORT(go->neg_upap) +
+#endif /* !EAP_SUPPORT && !CHAP_SUPPORT */
+#endif /* PAP_SUPPORT */
+#if LQR_SUPPORT
+ LENCILQR(go->neg_lqr) +
+#endif /* LQR_SUPPORT */
+ LENCICBCP(go->neg_cbcp) +
+ LENCILONG(go->neg_magicnumber) +
+ LENCIVOID(go->neg_pcompression) +
+ LENCIVOID(go->neg_accompression) +
+#ifdef HAVE_MULTILINK
+ LENCISHORT(go->neg_mrru) +
+#endif /* HAVE_MULTILINK */
+ LENCIVOID(go->neg_ssnhf) +
+ (go->neg_endpoint? CILEN_CHAR + go->endpoint.length: 0));
+}
+
+
+/*
+ * lcp_addci - Add our desired CIs to a packet.
+ */
+static void lcp_addci(fsm *f, u_char *ucp, int *lenp) {
+ ppp_pcb *pcb = f->pcb;
+ lcp_options *go = &pcb->lcp_gotoptions;
+ u_char *start_ucp = ucp;
+
+#define ADDCIVOID(opt, neg) \
+ if (neg) { \
+ PUTCHAR(opt, ucp); \
+ PUTCHAR(CILEN_VOID, ucp); \
+ }
+#define ADDCISHORT(opt, neg, val) \
+ if (neg) { \
+ PUTCHAR(opt, ucp); \
+ PUTCHAR(CILEN_SHORT, ucp); \
+ PUTSHORT(val, ucp); \
+ }
+#if CHAP_SUPPORT
+#define ADDCICHAP(opt, neg, val) \
+ if (neg) { \
+ PUTCHAR((opt), ucp); \
+ PUTCHAR(CILEN_CHAP, ucp); \
+ PUTSHORT(PPP_CHAP, ucp); \
+ PUTCHAR((CHAP_DIGEST(val)), ucp); \
+ }
+#endif /* CHAP_SUPPORT */
+#define ADDCILONG(opt, neg, val) \
+ if (neg) { \
+ PUTCHAR(opt, ucp); \
+ PUTCHAR(CILEN_LONG, ucp); \
+ PUTLONG(val, ucp); \
+ }
+#if LQR_SUPPORT
+#define ADDCILQR(opt, neg, val) \
+ if (neg) { \
+ PUTCHAR(opt, ucp); \
+ PUTCHAR(CILEN_LQR, ucp); \
+ PUTSHORT(PPP_LQR, ucp); \
+ PUTLONG(val, ucp); \
+ }
+#endif /* LQR_SUPPORT */
+#define ADDCICHAR(opt, neg, val) \
+ if (neg) { \
+ PUTCHAR(opt, ucp); \
+ PUTCHAR(CILEN_CHAR, ucp); \
+ PUTCHAR(val, ucp); \
+ }
+#define ADDCIENDP(opt, neg, class, val, len) \
+ if (neg) { \
+ int i; \
+ PUTCHAR(opt, ucp); \
+ PUTCHAR(CILEN_CHAR + len, ucp); \
+ PUTCHAR(class, ucp); \
+ for (i = 0; i < len; ++i) \
+ PUTCHAR(val[i], ucp); \
+ }
+
+ ADDCISHORT(CI_MRU, go->neg_mru && go->mru != PPP_DEFMRU, go->mru);
+ ADDCILONG(CI_ASYNCMAP, go->neg_asyncmap && go->asyncmap != 0xFFFFFFFF,
+ go->asyncmap);
+#if EAP_SUPPORT
+ ADDCISHORT(CI_AUTHTYPE, go->neg_eap, PPP_EAP);
+#endif /* EAP_SUPPORT */
+#if CHAP_SUPPORT /* cannot be improved, embedding a directive within macro arguments is not portable */
+#if EAP_SUPPORT
+ ADDCICHAP(CI_AUTHTYPE, !go->neg_eap && go->neg_chap, go->chap_mdtype);
+#endif /* EAP_SUPPORT */
+#if !EAP_SUPPORT
+ ADDCICHAP(CI_AUTHTYPE, go->neg_chap, go->chap_mdtype);
+#endif /* !EAP_SUPPORT */
+#endif /* CHAP_SUPPORT */
+#if PAP_SUPPORT /* cannot be improved, embedding a directive within macro arguments is not portable */
+#if EAP_SUPPORT && CHAP_SUPPORT
+ ADDCISHORT(CI_AUTHTYPE, !go->neg_eap && !go->neg_chap && go->neg_upap, PPP_PAP);
+#endif /* EAP_SUPPORT && CHAP_SUPPORT */
+#if EAP_SUPPORT && !CHAP_SUPPORT
+ ADDCISHORT(CI_AUTHTYPE, !go->neg_eap && go->neg_upap, PPP_PAP);
+#endif /* EAP_SUPPORT && !CHAP_SUPPORT */
+#if !EAP_SUPPORT && CHAP_SUPPORT
+ ADDCISHORT(CI_AUTHTYPE, !go->neg_chap && go->neg_upap, PPP_PAP);
+#endif /* !EAP_SUPPORT && CHAP_SUPPORT */
+#if !EAP_SUPPORT && !CHAP_SUPPORT
+ ADDCISHORT(CI_AUTHTYPE, go->neg_upap, PPP_PAP);
+#endif /* !EAP_SUPPORT && !CHAP_SUPPORT */
+#endif /* PAP_SUPPORT */
+#if LQR_SUPPORT
+ ADDCILQR(CI_QUALITY, go->neg_lqr, go->lqr_period);
+#endif /* LQR_SUPPORT */
+ 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);
+#ifdef HAVE_MULTILINK
+ ADDCISHORT(CI_MRRU, go->neg_mrru, go->mrru);
+#endif
+ ADDCIVOID(CI_SSNHF, go->neg_ssnhf);
+ ADDCIENDP(CI_EPDISC, go->neg_endpoint, go->endpoint.class_,
+ go->endpoint.value, go->endpoint.length);
+
+ if (ucp - start_ucp != *lenp) {
+ /* this should never happen, because peer_mtu should be 1500 */
+ ppp_error("Bug in lcp_addci: wrong length");
+ }
+}
+
+
+/*
+ * 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) {
+ ppp_pcb *pcb = f->pcb;
+ lcp_options *go = &pcb->lcp_gotoptions;
+ 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; \
+ }
+#if CHAP_SUPPORT
+#define ACKCICHAP(opt, neg, val) \
+ 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 != PPP_CHAP) \
+ goto bad; \
+ GETCHAR(cichar, p); \
+ if (cichar != (CHAP_DIGEST(val))) \
+ goto bad; \
+ }
+#endif /* CHAP_SUPPORT */
+#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; \
+ }
+#if LQR_SUPPORT
+#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; \
+ }
+#endif /* LQR_SUPPORT */
+#define ACKCIENDP(opt, neg, class, val, vlen) \
+ if (neg) { \
+ int i; \
+ if ((len -= CILEN_CHAR + vlen) < 0) \
+ goto bad; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != CILEN_CHAR + vlen || \
+ citype != opt) \
+ goto bad; \
+ GETCHAR(cichar, p); \
+ if (cichar != class) \
+ goto bad; \
+ for (i = 0; i < vlen; ++i) { \
+ GETCHAR(cichar, p); \
+ if (cichar != val[i]) \
+ goto bad; \
+ } \
+ }
+
+ ACKCISHORT(CI_MRU, go->neg_mru && go->mru != PPP_DEFMRU, go->mru);
+ ACKCILONG(CI_ASYNCMAP, go->neg_asyncmap && go->asyncmap != 0xFFFFFFFF,
+ go->asyncmap);
+#if EAP_SUPPORT
+ ACKCISHORT(CI_AUTHTYPE, go->neg_eap, PPP_EAP);
+#endif /* EAP_SUPPORT */
+#if CHAP_SUPPORT /* cannot be improved, embedding a directive within macro arguments is not portable */
+#if EAP_SUPPORT
+ ACKCICHAP(CI_AUTHTYPE, !go->neg_eap && go->neg_chap, go->chap_mdtype);
+#endif /* EAP_SUPPORT */
+#if !EAP_SUPPORT
+ ACKCICHAP(CI_AUTHTYPE, go->neg_chap, go->chap_mdtype);
+#endif /* !EAP_SUPPORT */
+#endif /* CHAP_SUPPORT */
+#if PAP_SUPPORT /* cannot be improved, embedding a directive within macro arguments is not portable */
+#if EAP_SUPPORT && CHAP_SUPPORT
+ ACKCISHORT(CI_AUTHTYPE, !go->neg_eap && !go->neg_chap && go->neg_upap, PPP_PAP);
+#endif /* EAP_SUPPORT && CHAP_SUPPORT */
+#if EAP_SUPPORT && !CHAP_SUPPORT
+ ACKCISHORT(CI_AUTHTYPE, !go->neg_eap && go->neg_upap, PPP_PAP);
+#endif /* EAP_SUPPORT && !CHAP_SUPPORT */
+#if !EAP_SUPPORT && CHAP_SUPPORT
+ ACKCISHORT(CI_AUTHTYPE, !go->neg_chap && go->neg_upap, PPP_PAP);
+#endif /* !EAP_SUPPORT && CHAP_SUPPORT */
+#if !EAP_SUPPORT && !CHAP_SUPPORT
+ ACKCISHORT(CI_AUTHTYPE, go->neg_upap, PPP_PAP);
+#endif /* !EAP_SUPPORT && !CHAP_SUPPORT */
+#endif /* PAP_SUPPORT */
+#if LQR_SUPPORT
+ ACKCILQR(CI_QUALITY, go->neg_lqr, go->lqr_period);
+#endif /* LQR_SUPPORT */
+ 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);
+#ifdef HAVE_MULTILINK
+ ACKCISHORT(CI_MRRU, go->neg_mrru, go->mrru);
+#endif /* HAVE_MULTILINK */
+ ACKCIVOID(CI_SSNHF, go->neg_ssnhf);
+ ACKCIENDP(CI_EPDISC, go->neg_endpoint, go->endpoint.class_,
+ go->endpoint.value, go->endpoint.length);
+
+ /*
+ * If there are any remaining CIs, then this packet is bad.
+ */
+ if (len != 0)
+ goto bad;
+ return (1);
+bad:
+ LCPDEBUG(("lcp_acki: received bad Ack!"));
+ 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 OPENED state.
+ *
+ * Returns:
+ * 0 - Nak was bad.
+ * 1 - Nak was good.
+ */
+static int lcp_nakci(fsm *f, u_char *p, int len, int treat_as_reject) {
+ ppp_pcb *pcb = f->pcb;
+ lcp_options *go = &pcb->lcp_gotoptions;
+ lcp_options *wo = &pcb->lcp_wantoptions;
+ 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) \
+ if (go->neg && \
+ len >= CILEN_VOID && \
+ p[1] == CILEN_VOID && \
+ p[0] == opt) { \
+ len -= CILEN_VOID; \
+ INCPTR(CILEN_VOID, p); \
+ no.neg = 1; \
+ try_.neg = 0; \
+ }
+#if CHAP_SUPPORT
+#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 \
+ }
+#endif /* CHAP_SUPPORT */
+#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 \
+ }
+#if LQR_SUPPORT
+#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 \
+ }
+#endif /* LQR_SUPPORT */
+#define NAKCIENDP(opt, neg) \
+ if (go->neg && \
+ len >= CILEN_CHAR && \
+ p[0] == opt && \
+ p[1] >= CILEN_CHAR && \
+ p[1] <= len) { \
+ len -= p[1]; \
+ INCPTR(p[1], p); \
+ no.neg = 1; \
+ try_.neg = 0; \
+ }
+
+ /*
+ * NOTE! There must be no assignments to individual fields of *go in
+ * the code below. Any such assignment is a BUG!
+ */
+ /*
+ * 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 != 0xFFFFFFFF) {
+ 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 ((0
+#if CHAP_SUPPORT
+ || go->neg_chap
+#endif /* CHAP_SUPPORT */
+#if PAP_SUPPORT
+ || go->neg_upap
+#endif /* PAP_SUPPORT */
+#if EAP_SUPPORT
+ || go->neg_eap
+#endif /* EAP_SUPPORT */
+ )
+ && len >= CILEN_SHORT
+ && p[0] == CI_AUTHTYPE && p[1] >= CILEN_SHORT && p[1] <= len) {
+ cilen = p[1];
+ len -= cilen;
+#if CHAP_SUPPORT
+ no.neg_chap = go->neg_chap;
+#endif /* CHAP_SUPPORT */
+#if PAP_SUPPORT
+ no.neg_upap = go->neg_upap;
+#endif /* PAP_SUPPORT */
+#if EAP_SUPPORT
+ no.neg_eap = go->neg_eap;
+#endif /* EAP_SUPPORT */
+ INCPTR(2, p);
+ GETSHORT(cishort, p);
+
+#if PAP_SUPPORT
+ if (cishort == PPP_PAP && cilen == CILEN_SHORT) {
+#if EAP_SUPPORT
+ /* If we were asking for EAP, then we need to stop that. */
+ if (go->neg_eap)
+ try_.neg_eap = 0;
+ else
+#endif /* EAP_SUPPORT */
+
+#if CHAP_SUPPORT
+ /* If we were asking for CHAP, then we need to stop that. */
+ if (go->neg_chap)
+ try_.neg_chap = 0;
+ else
+#endif /* CHAP_SUPPORT */
+
+ /*
+ * If we weren't asking for CHAP or EAP, then we were asking for
+ * PAP, in which case this Nak is bad.
+ */
+ goto bad;
+ } else
+#endif /* PAP_SUPPORT */
+
+#if CHAP_SUPPORT
+ if (cishort == PPP_CHAP && cilen == CILEN_CHAP) {
+ GETCHAR(cichar, p);
+#if EAP_SUPPORT
+ /* Stop asking for EAP, if we were. */
+ if (go->neg_eap) {
+ try_.neg_eap = 0;
+ /* Try to set up to use their suggestion, if possible */
+ if (CHAP_CANDIGEST(go->chap_mdtype, cichar))
+ try_.chap_mdtype = CHAP_MDTYPE_D(cichar);
+ } else
+#endif /* EAP_SUPPORT */
+ if (go->neg_chap) {
+ /*
+ * We were asking for our preferred algorithm, they must
+ * want something different.
+ */
+ if (cichar != CHAP_DIGEST(go->chap_mdtype)) {
+ if (CHAP_CANDIGEST(go->chap_mdtype, cichar)) {
+ /* Use their suggestion if we support it ... */
+ try_.chap_mdtype = CHAP_MDTYPE_D(cichar);
+ } else {
+ /* ... otherwise, try our next-preferred algorithm. */
+ try_.chap_mdtype &= ~(CHAP_MDTYPE(try_.chap_mdtype));
+ if (try_.chap_mdtype == MDTYPE_NONE) /* out of algos */
+ try_.neg_chap = 0;
+ }
+ } else {
+ /*
+ * Whoops, they Nak'd our algorithm of choice
+ * but then suggested it back to us.
+ */
+ goto bad;
+ }
+ } else {
+ /*
+ * Stop asking for PAP if we were asking for it.
+ */
+#if PAP_SUPPORT
+ try_.neg_upap = 0;
+#endif /* PAP_SUPPORT */
+ }
+
+ } else
+#endif /* CHAP_SUPPORT */
+ {
+
+#if EAP_SUPPORT
+ /*
+ * If we were asking for EAP, and they're Conf-Naking EAP,
+ * well, that's just strange. Nobody should do that.
+ */
+ if (cishort == PPP_EAP && cilen == CILEN_SHORT && go->neg_eap)
+ ppp_dbglog("Unexpected Conf-Nak for EAP");
+
+ /*
+ * We don't recognize what they're suggesting.
+ * Stop asking for what we were asking for.
+ */
+ if (go->neg_eap)
+ try_.neg_eap = 0;
+ else
+#endif /* EAP_SUPPORT */
+
+#if CHAP_SUPPORT
+ if (go->neg_chap)
+ try_.neg_chap = 0;
+ else
+#endif /* CHAP_SUPPORT */
+
+#if PAP_SUPPORT
+ if(1)
+ try_.neg_upap = 0;
+ else
+#endif /* PAP_SUPPORT */
+ {}
+
+ p += cilen - CILEN_SHORT;
+ }
+ }
+
+#if LQR_SUPPORT
+ /*
+ * 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;
+ );
+#endif /* LQR_SUPPORT */
+
+ /*
+ * Only implementing CBCP...not the rest of the callback options
+ */
+ NAKCICHAR(CI_CALLBACK, neg_cbcp,
+ try_.neg_cbcp = 0;
+ (void)cichar; /* if CHAP support is not compiled, cichar is set but not used, which makes some compilers complaining */
+ );
+
+ /*
+ * 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);
+ NAKCIVOID(CI_ACCOMPRESSION, neg_accompression);
+
+#ifdef HAVE_MULTILINK
+ /*
+ * Nak for MRRU option - accept their value if it is smaller
+ * than the one we want.
+ */
+ if (go->neg_mrru) {
+ NAKCISHORT(CI_MRRU, neg_mrru,
+ if (treat_as_reject)
+ try_.neg_mrru = 0;
+ else if (cishort <= wo->mrru)
+ try_.mrru = cishort;
+ );
+ }
+#else /* HAVE_MULTILINK */
+ LWIP_UNUSED_ARG(treat_as_reject);
+#endif /* HAVE_MULTILINK */
+
+ /*
+ * Nak for short sequence numbers shouldn't be sent, treat it
+ * like a reject.
+ */
+ NAKCIVOID(CI_SSNHF, neg_ssnhf);
+
+ /*
+ * Nak of the endpoint discriminator option is not permitted,
+ * treat it like a reject.
+ */
+ NAKCIENDP(CI_EPDISC, neg_endpoint);
+
+ /*
+ * 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_.neg_mru = 1;
+ try_.mru = cishort;
+ }
+ break;
+ case CI_ASYNCMAP:
+ if ((go->neg_asyncmap && go->asyncmap != 0xFFFFFFFF)
+ || no.neg_asyncmap || cilen != CILEN_LONG)
+ goto bad;
+ break;
+ case CI_AUTHTYPE:
+ if (0
+#if CHAP_SUPPORT
+ || go->neg_chap || no.neg_chap
+#endif /* CHAP_SUPPORT */
+#if PAP_SUPPORT
+ || go->neg_upap || no.neg_upap
+#endif /* PAP_SUPPORT */
+#if EAP_SUPPORT
+ || go->neg_eap || no.neg_eap
+#endif /* EAP_SUPPORT */
+ )
+ 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;
+#if LQR_SUPPORT
+ case CI_QUALITY:
+ if (go->neg_lqr || no.neg_lqr || cilen != CILEN_LQR)
+ goto bad;
+ break;
+#endif /* LQR_SUPPORT */
+#ifdef HAVE_MULTILINK
+ case CI_MRRU:
+ if (go->neg_mrru || no.neg_mrru || cilen != CILEN_SHORT)
+ goto bad;
+ break;
+#endif /* HAVE_MULTILINK */
+ case CI_SSNHF:
+ if (go->neg_ssnhf || no.neg_ssnhf || cilen != CILEN_VOID)
+ goto bad;
+ try_.neg_ssnhf = 1;
+ break;
+ case CI_EPDISC:
+ if (go->neg_endpoint || no.neg_endpoint || cilen < CILEN_CHAR)
+ goto bad;
+ break;
+ default:
+ break;
+ }
+ p = next;
+ }
+
+ /*
+ * OK, the Nak is good. Now we can update state.
+ * If there are any options left we ignore them.
+ */
+ if (f->state != PPP_FSM_OPENED) {
+ if (looped_back) {
+ if (++try_.numloops >= pcb->settings.lcp_loopbackfail) {
+ ppp_notice("Serial line is looped back.");
+ pcb->err_code = PPPERR_LOOPBACK;
+ lcp_close(f->pcb, "Loopback detected");
+ }
+ } else
+ try_.numloops = 0;
+ *go = try_;
+ }
+
+ return 1;
+
+bad:
+ LCPDEBUG(("lcp_nakci: received bad Nak!"));
+ 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 OPENED state.
+ *
+ * Returns:
+ * 0 - Reject was bad.
+ * 1 - Reject was good.
+ */
+static int lcp_rejci(fsm *f, u_char *p, int len) {
+ ppp_pcb *pcb = f->pcb;
+ lcp_options *go = &pcb->lcp_gotoptions;
+ 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; \
+ }
+#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; \
+ }
+
+#if CHAP_SUPPORT && EAP_SUPPORT && PAP_SUPPORT
+#define REJCICHAP(opt, neg, val) \
+ 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 != PPP_CHAP) || (cichar != (CHAP_DIGEST(val)))) \
+ goto bad; \
+ try_.neg = 0; \
+ try_.neg_eap = try_.neg_upap = 0; \
+ }
+#endif /* CHAP_SUPPORT && EAP_SUPPORT && PAP_SUPPORT */
+
+#if CHAP_SUPPORT && !EAP_SUPPORT && PAP_SUPPORT
+#define REJCICHAP(opt, neg, val) \
+ 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 != PPP_CHAP) || (cichar != (CHAP_DIGEST(val)))) \
+ goto bad; \
+ try_.neg = 0; \
+ try_.neg_upap = 0; \
+ }
+#endif /* CHAP_SUPPORT && !EAP_SUPPORT && PAP_SUPPORT */
+
+#if CHAP_SUPPORT && EAP_SUPPORT && !PAP_SUPPORT
+#define REJCICHAP(opt, neg, val) \
+ 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 != PPP_CHAP) || (cichar != (CHAP_DIGEST(val)))) \
+ goto bad; \
+ try_.neg = 0; \
+ try_.neg_eap = 0; \
+ }
+#endif /* CHAP_SUPPORT && EAP_SUPPORT && !PAP_SUPPORT */
+
+#if CHAP_SUPPORT && !EAP_SUPPORT && !PAP_SUPPORT
+#define REJCICHAP(opt, neg, val) \
+ 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 != PPP_CHAP) || (cichar != (CHAP_DIGEST(val)))) \
+ goto bad; \
+ try_.neg = 0; \
+ }
+#endif /* CHAP_SUPPORT && !EAP_SUPPORT && !PAP_SUPPORT */
+
+#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; \
+ }
+#if LQR_SUPPORT
+#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; \
+ }
+#endif /* LQR_SUPPORT */
+#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; \
+ }
+#define REJCIENDP(opt, neg, class, val, vlen) \
+ if (go->neg && \
+ len >= CILEN_CHAR + vlen && \
+ p[0] == opt && \
+ p[1] == CILEN_CHAR + vlen) { \
+ int i; \
+ len -= CILEN_CHAR + vlen; \
+ INCPTR(2, p); \
+ GETCHAR(cichar, p); \
+ if (cichar != class) \
+ goto bad; \
+ for (i = 0; i < vlen; ++i) { \
+ GETCHAR(cichar, p); \
+ if (cichar != val[i]) \
+ goto bad; \
+ } \
+ try_.neg = 0; \
+ }
+
+ REJCISHORT(CI_MRU, neg_mru, go->mru);
+ REJCILONG(CI_ASYNCMAP, neg_asyncmap, go->asyncmap);
+#if EAP_SUPPORT
+ REJCISHORT(CI_AUTHTYPE, neg_eap, PPP_EAP);
+ if (!go->neg_eap) {
+#endif /* EAP_SUPPORT */
+#if CHAP_SUPPORT
+ REJCICHAP(CI_AUTHTYPE, neg_chap, go->chap_mdtype);
+ if (!go->neg_chap) {
+#endif /* CHAP_SUPPORT */
+#if PAP_SUPPORT
+ REJCISHORT(CI_AUTHTYPE, neg_upap, PPP_PAP);
+#endif /* PAP_SUPPORT */
+#if CHAP_SUPPORT
+ }
+#endif /* CHAP_SUPPORT */
+#if EAP_SUPPORT
+ }
+#endif /* EAP_SUPPORT */
+#if LQR_SUPPORT
+ REJCILQR(CI_QUALITY, neg_lqr, go->lqr_period);
+#endif /* LQR_SUPPORT */
+ REJCICBCP(CI_CALLBACK, neg_cbcp, CBCP_OPT);
+ REJCILONG(CI_MAGICNUMBER, neg_magicnumber, go->magicnumber);
+ REJCIVOID(CI_PCOMPRESSION, neg_pcompression);
+ REJCIVOID(CI_ACCOMPRESSION, neg_accompression);
+#ifdef HAVE_MULTILINK
+ REJCISHORT(CI_MRRU, neg_mrru, go->mrru);
+#endif /* HAVE_MULTILINK */
+ REJCIVOID(CI_SSNHF, neg_ssnhf);
+ REJCIENDP(CI_EPDISC, neg_endpoint, go->endpoint.class_,
+ go->endpoint.value, go->endpoint.length);
+
+ /*
+ * If there are any remaining CIs, then this packet is bad.
+ */
+ if (len != 0)
+ goto bad;
+ /*
+ * Now we can update state.
+ */
+ if (f->state != PPP_FSM_OPENED)
+ *go = try_;
+ return 1;
+
+bad:
+ LCPDEBUG(("lcp_rejci: received bad Reject!"));
+ 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.
+ *
+ * inp = Requested CIs
+ * lenp = Length of requested CIs
+ */
+static int lcp_reqci(fsm *f, u_char *inp, int *lenp, int reject_if_disagree) {
+ ppp_pcb *pcb = f->pcb;
+ lcp_options *go = &pcb->lcp_gotoptions;
+ lcp_options *ho = &pcb->lcp_hisoptions;
+ lcp_options *ao = &pcb->lcp_allowoptions;
+ u_char *cip, *next; /* Pointer to current and next CIs */
+ int cilen, citype, cichar; /* Parsed len, type, 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 */
+ struct pbuf *nakp; /* Nak buffer */
+ u_char *nakoutp; /* Pointer to next char in Nak frame */
+ int l = *lenp; /* Length left */
+
+ /*
+ * Reset all his options.
+ */
+ BZERO(ho, sizeof(*ho));
+
+ /*
+ * Process all his options.
+ */
+ next = inp;
+ nakp = pbuf_alloc(PBUF_RAW, (u16_t)(PPP_CTRL_PBUF_MAX_SIZE), PPP_CTRL_PBUF_TYPE);
+ if(NULL == nakp)
+ return 0;
+ if(nakp->tot_len != nakp->len) {
+ pbuf_free(nakp);
+ return 0;
+ }
+
+ nakoutp = (u_char*)nakp->payload;
+ 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(("lcp_reqci: bad CI length!"));
+ 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? */
+ cilen != CILEN_SHORT) { /* Check CI length */
+ 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) {
+ orc = CONFNAK; /* Nak CI */
+ PUTCHAR(CI_MRU, nakoutp);
+ PUTCHAR(CILEN_SHORT, nakoutp);
+ PUTSHORT(PPP_MINMRU, nakoutp); /* Give him a hint */
+ break;
+ }
+ ho->neg_mru = 1; /* Remember he sent MRU */
+ ho->mru = cishort; /* And remember value */
+ break;
+
+ case CI_ASYNCMAP:
+ if (!ao->neg_asyncmap ||
+ cilen != CILEN_LONG) {
+ 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) {
+ orc = CONFNAK;
+ PUTCHAR(CI_ASYNCMAP, nakoutp);
+ PUTCHAR(CILEN_LONG, nakoutp);
+ PUTLONG(ao->asyncmap | cilong, nakoutp);
+ break;
+ }
+ ho->neg_asyncmap = 1;
+ ho->asyncmap = cilong;
+ break;
+
+ case CI_AUTHTYPE:
+ if (cilen < CILEN_SHORT ||
+ !(0
+#if PAP_SUPPORT
+ || ao->neg_upap
+#endif /* PAP_SUPPORT */
+#if CHAP_SUPPORT
+ || ao->neg_chap
+#endif /* CHAP_SUPPORT */
+#if EAP_SUPPORT
+ || ao->neg_eap
+#endif /* EAP_SUPPORT */
+ )) {
+ /*
+ * Reject the option if we're not willing to authenticate.
+ */
+ ppp_dbglog("No auth is possible");
+ orc = CONFREJ;
+ break;
+ }
+ GETSHORT(cishort, p);
+
+ /*
+ * Authtype must be PAP, CHAP, or EAP.
+ *
+ * Note: if more than one of ao->neg_upap, ao->neg_chap, and
+ * ao->neg_eap are set, and the peer sends a Configure-Request
+ * with two or more authenticate-protocol requests, then we will
+ * reject the second request.
+ * Whether we end up doing CHAP, UPAP, or EAP depends then on
+ * the ordering of the CIs in the peer's Configure-Request.
+ */
+
+#if PAP_SUPPORT
+ if (cishort == PPP_PAP) {
+ /* we've already accepted CHAP or EAP */
+ if (0
+#if CHAP_SUPPORT
+ || ho->neg_chap
+#endif /* CHAP_SUPPORT */
+#if EAP_SUPPORT
+ || ho->neg_eap
+#endif /* EAP_SUPPORT */
+ || cilen != CILEN_SHORT) {
+ LCPDEBUG(("lcp_reqci: rcvd AUTHTYPE PAP, rejecting..."));
+ orc = CONFREJ;
+ break;
+ }
+ if (!ao->neg_upap) { /* we don't want to do PAP */
+ orc = CONFNAK; /* NAK it and suggest CHAP or EAP */
+ PUTCHAR(CI_AUTHTYPE, nakoutp);
+#if EAP_SUPPORT
+ if (ao->neg_eap) {
+ PUTCHAR(CILEN_SHORT, nakoutp);
+ PUTSHORT(PPP_EAP, nakoutp);
+ } else {
+#endif /* EAP_SUPPORT */
+#if CHAP_SUPPORT
+ PUTCHAR(CILEN_CHAP, nakoutp);
+ PUTSHORT(PPP_CHAP, nakoutp);
+ PUTCHAR(CHAP_DIGEST(ao->chap_mdtype), nakoutp);
+#endif /* CHAP_SUPPORT */
+#if EAP_SUPPORT
+ }
+#endif /* EAP_SUPPORT */
+ break;
+ }
+ ho->neg_upap = 1;
+ break;
+ }
+#endif /* PAP_SUPPORT */
+#if CHAP_SUPPORT
+ if (cishort == PPP_CHAP) {
+ /* we've already accepted PAP or EAP */
+ if (
+#if PAP_SUPPORT
+ ho->neg_upap ||
+#endif /* PAP_SUPPORT */
+#if EAP_SUPPORT
+ ho->neg_eap ||
+#endif /* EAP_SUPPORT */
+ cilen != CILEN_CHAP) {
+ LCPDEBUG(("lcp_reqci: rcvd AUTHTYPE CHAP, rejecting..."));
+ orc = CONFREJ;
+ break;
+ }
+ if (!ao->neg_chap) { /* we don't want to do CHAP */
+ orc = CONFNAK; /* NAK it and suggest EAP or PAP */
+ PUTCHAR(CI_AUTHTYPE, nakoutp);
+ PUTCHAR(CILEN_SHORT, nakoutp);
+#if EAP_SUPPORT
+ if (ao->neg_eap) {
+ PUTSHORT(PPP_EAP, nakoutp);
+ } else
+#endif /* EAP_SUPPORT */
+#if PAP_SUPPORT
+ if(1) {
+ PUTSHORT(PPP_PAP, nakoutp);
+ }
+ else
+#endif /* PAP_SUPPORT */
+ {}
+ break;
+ }
+ GETCHAR(cichar, p); /* get digest type */
+ if (!(CHAP_CANDIGEST(ao->chap_mdtype, cichar))) {
+ /*
+ * We can't/won't do the requested type,
+ * suggest something else.
+ */
+ orc = CONFNAK;
+ PUTCHAR(CI_AUTHTYPE, nakoutp);
+ PUTCHAR(CILEN_CHAP, nakoutp);
+ PUTSHORT(PPP_CHAP, nakoutp);
+ PUTCHAR(CHAP_DIGEST(ao->chap_mdtype), nakoutp);
+ break;
+ }
+ ho->chap_mdtype = CHAP_MDTYPE_D(cichar); /* save md type */
+ ho->neg_chap = 1;
+ break;
+ }
+#endif /* CHAP_SUPPORT */
+#if EAP_SUPPORT
+ if (cishort == PPP_EAP) {
+ /* we've already accepted CHAP or PAP */
+ if (
+#if CHAP_SUPPORT
+ ho->neg_chap ||
+#endif /* CHAP_SUPPORT */
+#if PAP_SUPPORT
+ ho->neg_upap ||
+#endif /* PAP_SUPPORT */
+ cilen != CILEN_SHORT) {
+ LCPDEBUG(("lcp_reqci: rcvd AUTHTYPE EAP, rejecting..."));
+ orc = CONFREJ;
+ break;
+ }
+ if (!ao->neg_eap) { /* we don't want to do EAP */
+ orc = CONFNAK; /* NAK it and suggest CHAP or PAP */
+ PUTCHAR(CI_AUTHTYPE, nakoutp);
+#if CHAP_SUPPORT
+ if (ao->neg_chap) {
+ PUTCHAR(CILEN_CHAP, nakoutp);
+ PUTSHORT(PPP_CHAP, nakoutp);
+ PUTCHAR(CHAP_DIGEST(ao->chap_mdtype), nakoutp);
+ } else
+#endif /* CHAP_SUPPORT */
+#if PAP_SUPPORT
+ if(1) {
+ PUTCHAR(CILEN_SHORT, nakoutp);
+ PUTSHORT(PPP_PAP, nakoutp);
+ } else
+#endif /* PAP_SUPPORT */
+ {}
+ break;
+ }
+ ho->neg_eap = 1;
+ break;
+ }
+#endif /* EAP_SUPPORT */
+
+ /*
+ * 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 ||
+ * ao->neg_eap.)
+ */
+ orc = CONFNAK;
+ PUTCHAR(CI_AUTHTYPE, nakoutp);
+
+#if EAP_SUPPORT
+ if (ao->neg_eap) {
+ PUTCHAR(CILEN_SHORT, nakoutp);
+ PUTSHORT(PPP_EAP, nakoutp);
+ } else
+#endif /* EAP_SUPPORT */
+#if CHAP_SUPPORT
+ if (ao->neg_chap) {
+ PUTCHAR(CILEN_CHAP, nakoutp);
+ PUTSHORT(PPP_CHAP, nakoutp);
+ PUTCHAR(CHAP_DIGEST(ao->chap_mdtype), nakoutp);
+ } else
+#endif /* CHAP_SUPPORT */
+#if PAP_SUPPORT
+ if(1) {
+ PUTCHAR(CILEN_SHORT, nakoutp);
+ PUTSHORT(PPP_PAP, nakoutp);
+ } else
+#endif /* PAP_SUPPORT */
+ {}
+ break;
+
+#if LQR_SUPPORT
+ case CI_QUALITY:
+ if (!ao->neg_lqr ||
+ cilen != CILEN_LQR) {
+ orc = CONFREJ;
+ break;
+ }
+
+ GETSHORT(cishort, p);
+ GETLONG(cilong, p);
+
+ /*
+ * 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, nakoutp);
+ PUTCHAR(CILEN_LQR, nakoutp);
+ PUTSHORT(PPP_LQR, nakoutp);
+ PUTLONG(ao->lqr_period, nakoutp);
+ break;
+ }
+ break;
+#endif /* LQR_SUPPORT */
+
+ case CI_MAGICNUMBER:
+ if (!(ao->neg_magicnumber || go->neg_magicnumber) ||
+ cilen != CILEN_LONG) {
+ orc = CONFREJ;
+ break;
+ }
+ GETLONG(cilong, p);
+
+ /*
+ * 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, nakoutp);
+ PUTCHAR(CILEN_LONG, nakoutp);
+ PUTLONG(cilong, nakoutp);
+ break;
+ }
+ ho->neg_magicnumber = 1;
+ ho->magicnumber = cilong;
+ break;
+
+
+ case CI_PCOMPRESSION:
+ if (!ao->neg_pcompression ||
+ cilen != CILEN_VOID) {
+ orc = CONFREJ;
+ break;
+ }
+ ho->neg_pcompression = 1;
+ break;
+
+ case CI_ACCOMPRESSION:
+ if (!ao->neg_accompression ||
+ cilen != CILEN_VOID) {
+ orc = CONFREJ;
+ break;
+ }
+ ho->neg_accompression = 1;
+ break;
+
+#ifdef HAVE_MULTILINK
+ case CI_MRRU:
+ if (!ao->neg_mrru
+ || !multilink
+ || cilen != CILEN_SHORT) {
+ orc = CONFREJ;
+ break;
+ }
+
+ GETSHORT(cishort, p);
+ /* possibly should insist on a minimum/maximum MRRU here */
+ ho->neg_mrru = 1;
+ ho->mrru = cishort;
+ break;
+#endif /* HAVE_MULTILINK */
+
+ case CI_SSNHF:
+ if (!ao->neg_ssnhf
+#ifdef HAVE_MULTILINK
+ || !multilink
+#endif /* HAVE_MULTILINK */
+ || cilen != CILEN_VOID) {
+ orc = CONFREJ;
+ break;
+ }
+ ho->neg_ssnhf = 1;
+ break;
+
+ case CI_EPDISC:
+ if (!ao->neg_endpoint ||
+ cilen < CILEN_CHAR ||
+ cilen > CILEN_CHAR + MAX_ENDP_LEN) {
+ orc = CONFREJ;
+ break;
+ }
+ GETCHAR(cichar, p);
+ cilen -= CILEN_CHAR;
+ ho->neg_endpoint = 1;
+ ho->endpoint.class_ = cichar;
+ ho->endpoint.length = cilen;
+ MEMCPY(ho->endpoint.value, p, cilen);
+ INCPTR(cilen, p);
+ break;
+
+ default:
+ LCPDEBUG(("lcp_reqci: rcvd unknown option %d", 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? */
+ && 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? */
+ MEMCPY(rejp, cip, 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 *nakoutp.
+ * At present there are no cases where we want to ask the
+ * peer to negotiate an option.
+ */
+
+ switch (rc) {
+ case CONFACK:
+ *lenp = next - inp;
+ break;
+ case CONFNAK:
+ /*
+ * Copy the Nak'd options from the nak buffer to the caller's buffer.
+ */
+ *lenp = nakoutp - (u_char*)nakp->payload;
+ MEMCPY(inp, nakp->payload, *lenp);
+ break;
+ case CONFREJ:
+ *lenp = rejp - inp;
+ break;
+ default:
+ break;
+ }
+
+ pbuf_free(nakp);
+ LCPDEBUG(("lcp_reqci: returning CONF%s.", CODENAME(rc)));
+ return (rc); /* Return final code */
+}
+
+
+/*
+ * lcp_up - LCP has come UP.
+ */
+static void lcp_up(fsm *f) {
+ ppp_pcb *pcb = f->pcb;
+ lcp_options *wo = &pcb->lcp_wantoptions;
+ lcp_options *ho = &pcb->lcp_hisoptions;
+ lcp_options *go = &pcb->lcp_gotoptions;
+ lcp_options *ao = &pcb->lcp_allowoptions;
+ int mtu, mru;
+
+ 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.
+ * Note on the MTU: the link MTU can be the MRU the peer wanted,
+ * the interface MTU is set to the lowest of that, the
+ * MTU we want to use, and our link MRU.
+ */
+ mtu = ho->neg_mru? ho->mru: PPP_MRU;
+ mru = go->neg_mru? LWIP_MAX(wo->mru, go->mru): PPP_MRU;
+#ifdef HAVE_MULTILINK
+ if (!(multilink && go->neg_mrru && ho->neg_mrru))
+#endif /* HAVE_MULTILINK */
+ netif_set_mtu(pcb, LWIP_MIN(LWIP_MIN(mtu, mru), ao->mru));
+ ppp_send_config(pcb, mtu,
+ (ho->neg_asyncmap? ho->asyncmap: 0xffffffff),
+ ho->neg_pcompression, ho->neg_accompression);
+ ppp_recv_config(pcb, mru,
+ (pcb->settings.lax_recv? 0: go->neg_asyncmap? go->asyncmap: 0xffffffff),
+ go->neg_pcompression, go->neg_accompression);
+
+ if (ho->neg_mru)
+ pcb->peer_mru = ho->mru;
+
+ lcp_echo_lowerup(f->pcb); /* Enable echo messages */
+
+ link_established(pcb);
+}
+
+
+/*
+ * lcp_down - LCP has gone DOWN.
+ *
+ * Alert other protocols.
+ */
+static void lcp_down(fsm *f) {
+ ppp_pcb *pcb = f->pcb;
+ lcp_options *go = &pcb->lcp_gotoptions;
+
+ lcp_echo_lowerdown(f->pcb);
+
+ link_down(pcb);
+
+ ppp_send_config(pcb, PPP_MRU, 0xffffffff, 0, 0);
+ ppp_recv_config(pcb, PPP_MRU,
+ (go->neg_asyncmap? go->asyncmap: 0xffffffff),
+ go->neg_pcompression, go->neg_accompression);
+ pcb->peer_mru = PPP_MRU;
+}
+
+
+/*
+ * lcp_starting - LCP needs the lower layer up.
+ */
+static void lcp_starting(fsm *f) {
+ ppp_pcb *pcb = f->pcb;
+ link_required(pcb);
+}
+
+
+/*
+ * lcp_finished - LCP has finished with the lower layer.
+ */
+static void lcp_finished(fsm *f) {
+ ppp_pcb *pcb = f->pcb;
+ link_terminated(pcb);
+}
+
+
+#if PRINTPKT_SUPPORT
+/*
+ * lcp_printpkt - print the contents of an LCP packet.
+ */
+static const char* const lcp_codenames[] = {
+ "ConfReq", "ConfAck", "ConfNak", "ConfRej",
+ "TermReq", "TermAck", "CodeRej", "ProtRej",
+ "EchoReq", "EchoRep", "DiscReq", "Ident",
+ "TimeRem"
+};
+
+static int lcp_printpkt(const u_char *p, int plen,
+ void (*printer) (void *, const char *, ...), void *arg) {
+ int code, id, len, olen, i;
+ const 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 <= (int)LWIP_ARRAYSIZE(lcp_codenames))
+ 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%x", cilong);
+ }
+ break;
+ case CI_AUTHTYPE:
+ if (olen >= CILEN_SHORT) {
+ p += 2;
+ printer(arg, "auth ");
+ GETSHORT(cishort, p);
+ switch (cishort) {
+#if PAP_SUPPORT
+ case PPP_PAP:
+ printer(arg, "pap");
+ break;
+#endif /* PAP_SUPPORT */
+#if CHAP_SUPPORT
+ case PPP_CHAP:
+ printer(arg, "chap");
+ if (p < optend) {
+ switch (*p) {
+ case CHAP_MD5:
+ printer(arg, " MD5");
+ ++p;
+ break;
+#if MSCHAP_SUPPORT
+ case CHAP_MICROSOFT:
+ printer(arg, " MS");
+ ++p;
+ break;
+
+ case CHAP_MICROSOFT_V2:
+ printer(arg, " MS-v2");
+ ++p;
+ break;
+#endif /* MSCHAP_SUPPORT */
+ default:
+ break;
+ }
+ }
+ break;
+#endif /* CHAP_SUPPORT */
+#if EAP_SUPPORT
+ case PPP_EAP:
+ printer(arg, "eap");
+ break;
+#endif /* EAP_SUPPORT */
+ default:
+ printer(arg, "0x%x", cishort);
+ }
+ }
+ break;
+#if LQR_SUPPORT
+ 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;
+#endif /* LQR_SUPPORT */
+ case CI_CALLBACK:
+ if (olen >= CILEN_CHAR) {
+ p += 2;
+ printer(arg, "callback ");
+ GETCHAR(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;
+ case CI_MRRU:
+ if (olen == CILEN_SHORT) {
+ p += 2;
+ GETSHORT(cishort, p);
+ printer(arg, "mrru %d", cishort);
+ }
+ break;
+ case CI_SSNHF:
+ if (olen == CILEN_VOID) {
+ p += 2;
+ printer(arg, "ssnhf");
+ }
+ break;
+ case CI_EPDISC:
+#ifdef HAVE_MULTILINK
+ if (olen >= CILEN_CHAR) {
+ struct epdisc epd;
+ p += 2;
+ GETCHAR(epd.class, p);
+ epd.length = olen - CILEN_CHAR;
+ if (epd.length > MAX_ENDP_LEN)
+ epd.length = MAX_ENDP_LEN;
+ if (epd.length > 0) {
+ MEMCPY(epd.value, p, epd.length);
+ p += epd.length;
+ }
+ printer(arg, "endpoint [%s]", epdisc_to_str(&epd));
+ }
+#else
+ printer(arg, "endpoint");
+#endif
+ break;
+ default:
+ 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, " ");
+ ppp_print_string(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);
+ len -= 4;
+ }
+ break;
+
+ case IDENTIF:
+ case TIMEREM:
+ if (len >= 4) {
+ GETLONG(cilong, p);
+ printer(arg, " magic=0x%x", cilong);
+ len -= 4;
+ }
+ if (code == TIMEREM) {
+ if (len < 4)
+ break;
+ GETLONG(cilong, p);
+ printer(arg, " seconds=%u", cilong);
+ len -= 4;
+ }
+ if (len > 0) {
+ printer(arg, " ");
+ ppp_print_string(p, len, printer, arg);
+ p += len;
+ len = 0;
+ }
+ break;
+ default:
+ break;
+ }
+
+ /* print the rest of the bytes in the packet */
+ for (i = 0; i < len && i < 32; ++i) {
+ GETCHAR(code, p);
+ printer(arg, " %.2x", code);
+ }
+ if (i < len) {
+ printer(arg, " ...");
+ p += len - i;
+ }
+
+ return p - pstart;
+}
+#endif /* PRINTPKT_SUPPORT */
+
+/*
+ * Time to shut down the link because there is nothing out there.
+ */
+
+static void LcpLinkFailure(fsm *f) {
+ ppp_pcb *pcb = f->pcb;
+ if (f->state == PPP_FSM_OPENED) {
+ ppp_info("No response to %d echo-requests", pcb->lcp_echos_pending);
+ ppp_notice("Serial link appears to be disconnected.");
+ pcb->err_code = PPPERR_PEERDEAD;
+ lcp_close(pcb, "Peer not responding");
+ }
+}
+
+/*
+ * Timer expired for the LCP echo requests from this process.
+ */
+
+static void LcpEchoCheck(fsm *f) {
+ ppp_pcb *pcb = f->pcb;
+
+ LcpSendEchoRequest (f);
+ if (f->state != PPP_FSM_OPENED)
+ return;
+
+ /*
+ * Start the timer for the next interval.
+ */
+ if (pcb->lcp_echo_timer_running)
+ ppp_warn("assertion lcp_echo_timer_running==0 failed");
+ TIMEOUT (LcpEchoTimeout, f, pcb->settings.lcp_echo_interval);
+ pcb->lcp_echo_timer_running = 1;
+}
+
+/*
+ * LcpEchoTimeout - Timer expired on the LCP echo
+ */
+
+static void LcpEchoTimeout(void *arg) {
+ fsm *f = (fsm*)arg;
+ ppp_pcb *pcb = f->pcb;
+ if (pcb->lcp_echo_timer_running != 0) {
+ pcb->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) {
+ ppp_pcb *pcb = f->pcb;
+ lcp_options *go = &pcb->lcp_gotoptions;
+ u32_t magic_val;
+ LWIP_UNUSED_ARG(id);
+
+ /* Check the magic number - don't count replies from ourselves. */
+ if (len < 4) {
+ ppp_dbglog("lcp: received short Echo-Reply, length %d", len);
+ return;
+ }
+ GETLONG(magic_val, inp);
+ if (go->neg_magicnumber
+ && magic_val == go->magicnumber) {
+ ppp_warn("appear to have received our own echo-reply!");
+ return;
+ }
+
+ /* Reset the number of outstanding echo frames */
+ pcb->lcp_echos_pending = 0;
+}
+
+/*
+ * LcpSendEchoRequest - Send an echo request frame to the peer
+ */
+
+static void LcpSendEchoRequest(fsm *f) {
+ ppp_pcb *pcb = f->pcb;
+ lcp_options *go = &pcb->lcp_gotoptions;
+ u32_t lcp_magic;
+ u_char pkt[4], *pktp;
+
+ /*
+ * Detect the failure of the peer at this point.
+ */
+ if (pcb->settings.lcp_echo_fails != 0) {
+ if (pcb->lcp_echos_pending >= pcb->settings.lcp_echo_fails) {
+ LcpLinkFailure(f);
+ pcb->lcp_echos_pending = 0;
+ }
+ }
+
+#if PPP_LCP_ADAPTIVE
+ /*
+ * If adaptive echos have been enabled, only send the echo request if
+ * no traffic was received since the last one.
+ */
+ if (pcb->settings.lcp_echo_adaptive) {
+ static unsigned int last_pkts_in = 0;
+
+#if PPP_STATS_SUPPORT
+ update_link_stats(f->unit);
+ link_stats_valid = 0;
+#endif /* PPP_STATS_SUPPORT */
+
+ if (link_stats.pkts_in != last_pkts_in) {
+ last_pkts_in = link_stats.pkts_in;
+ return;
+ }
+ }
+#endif
+
+ /*
+ * Make and send the echo request frame.
+ */
+ if (f->state == PPP_FSM_OPENED) {
+ lcp_magic = go->magicnumber;
+ pktp = pkt;
+ PUTLONG(lcp_magic, pktp);
+ fsm_sdata(f, ECHOREQ, pcb->lcp_echo_number++, pkt, pktp - pkt);
+ ++pcb->lcp_echos_pending;
+ }
+}
+
+/*
+ * lcp_echo_lowerup - Start the timer for the LCP frame
+ */
+
+static void lcp_echo_lowerup(ppp_pcb *pcb) {
+ fsm *f = &pcb->lcp_fsm;
+
+ /* Clear the parameters for generating echo frames */
+ pcb->lcp_echos_pending = 0;
+ pcb->lcp_echo_number = 0;
+ pcb->lcp_echo_timer_running = 0;
+
+ /* If a timeout interval is specified then start the timer */
+ if (pcb->settings.lcp_echo_interval != 0)
+ LcpEchoCheck (f);
+}
+
+/*
+ * lcp_echo_lowerdown - Stop the timer for the LCP frame
+ */
+
+static void lcp_echo_lowerdown(ppp_pcb *pcb) {
+ fsm *f = &pcb->lcp_fsm;
+
+ if (pcb->lcp_echo_timer_running != 0) {
+ UNTIMEOUT (LcpEchoTimeout, f);
+ pcb->lcp_echo_timer_running = 0;
+ }
+}
+
+#endif /* PPP_SUPPORT */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/magic.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/magic.c
new file mode 100644
index 0000000..d0d87c5
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/magic.c
@@ -0,0 +1,294 @@
+/*
+ * magic.c - PPP Magic Number routines.
+ *
+ * Copyright (c) 1984-2000 Carnegie Mellon University. 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 "Carnegie Mellon University" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission. For permission or any legal
+ * details, please contact
+ * Office of Technology Transfer
+ * Carnegie Mellon University
+ * 5000 Forbes Avenue
+ * Pittsburgh, PA 15213-3890
+ * (412) 268-4387, fax: (412) 268-7395
+ * tech-transfer@andrew.cmu.edu
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by Computing Services
+ * at Carnegie Mellon University (http://www.cmu.edu/computing/)."
+ *
+ * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
+ * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+/*****************************************************************************
+* 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 "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#include "netif/ppp/ppp_impl.h"
+#include "netif/ppp/magic.h"
+
+#if PPP_MD5_RANDM /* Using MD5 for better randomness if enabled */
+
+#include "netif/ppp/pppcrypt.h"
+
+#define MD5_HASH_SIZE 16
+static char magic_randpool[MD5_HASH_SIZE]; /* Pool of randomness. */
+static long magic_randcount; /* Pseudo-random incrementer */
+static u32_t magic_randomseed; /* Seed used for random number generation. */
+
+/*
+ * 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
+ */
+static void magic_churnrand(char *rand_data, u32_t rand_len) {
+ lwip_md5_context md5_ctx;
+
+ /* LWIP_DEBUGF(LOG_INFO, ("magic_churnrand: %u@%P\n", rand_len, rand_data)); */
+ lwip_md5_init(&md5_ctx);
+ lwip_md5_starts(&md5_ctx);
+ lwip_md5_update(&md5_ctx, (u_char *)magic_randpool, sizeof(magic_randpool));
+ if (rand_data) {
+ lwip_md5_update(&md5_ctx, (u_char *)rand_data, rand_len);
+ } else {
+ struct {
+ /* INCLUDE fields for any system sources of randomness */
+ u32_t jiffies;
+#ifdef LWIP_RAND
+ u32_t rand;
+#endif /* LWIP_RAND */
+ } sys_data;
+ magic_randomseed += sys_jiffies();
+ sys_data.jiffies = magic_randomseed;
+#ifdef LWIP_RAND
+ sys_data.rand = LWIP_RAND();
+#endif /* LWIP_RAND */
+ /* Load sys_data fields here. */
+ lwip_md5_update(&md5_ctx, (u_char *)&sys_data, sizeof(sys_data));
+ }
+ lwip_md5_finish(&md5_ctx, (u_char *)magic_randpool);
+ lwip_md5_free(&md5_ctx);
+/* LWIP_DEBUGF(LOG_INFO, ("magic_churnrand: -> 0\n")); */
+}
+
+/*
+ * Initialize the random number generator.
+ */
+void magic_init(void) {
+ magic_churnrand(NULL, 0);
+}
+
+/*
+ * Randomize our random seed value.
+ */
+void magic_randomize(void) {
+ magic_churnrand(NULL, 0);
+}
+
+/*
+ * magic_random_bytes - Fill a buffer with random bytes.
+ *
+ * Use the random pool to generate random data. This degrades to pseudo
+ * random when used faster than randomness is supplied using magic_churnrand().
+ * Note: It's important that there be sufficient randomness in magic_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 magic_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
+ * magic_randcount each time? Probably there is a weakness but I wish that
+ * it was documented.
+ */
+void magic_random_bytes(unsigned char *buf, u32_t buf_len) {
+ lwip_md5_context md5_ctx;
+ u_char tmp[MD5_HASH_SIZE];
+ u32_t n;
+
+ while (buf_len > 0) {
+ lwip_md5_init(&md5_ctx);
+ lwip_md5_starts(&md5_ctx);
+ lwip_md5_update(&md5_ctx, (u_char *)magic_randpool, sizeof(magic_randpool));
+ lwip_md5_update(&md5_ctx, (u_char *)&magic_randcount, sizeof(magic_randcount));
+ lwip_md5_finish(&md5_ctx, tmp);
+ lwip_md5_free(&md5_ctx);
+ magic_randcount++;
+ n = LWIP_MIN(buf_len, MD5_HASH_SIZE);
+ MEMCPY(buf, tmp, n);
+ buf += n;
+ buf_len -= n;
+ }
+}
+
+/*
+ * Return a new random number.
+ */
+u32_t magic(void) {
+ u32_t new_rand;
+
+ magic_random_bytes((unsigned char *)&new_rand, sizeof(new_rand));
+
+ return new_rand;
+}
+
+#else /* PPP_MD5_RANDM */
+
+/*****************************/
+/*** LOCAL DATA STRUCTURES ***/
+/*****************************/
+#ifndef LWIP_RAND
+static int magic_randomized; /* Set when truely randomized. */
+#endif /* LWIP_RAND */
+static u32_t magic_randomseed; /* 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 magic_init(void) {
+ magic_randomseed += sys_jiffies();
+#ifndef LWIP_RAND
+ /* Initialize the Borland random number generator. */
+ srand((unsigned)magic_randomseed);
+#endif /* LWIP_RAND */
+}
+
+/*
+ * magic_init - Initialize the magic number generator.
+ *
+ * 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 magic_randomize(void) {
+#ifndef LWIP_RAND
+ if (!magic_randomized) {
+ magic_randomized = !0;
+ magic_init();
+ /* The initialization function also updates the seed. */
+ } else {
+#endif /* LWIP_RAND */
+ magic_randomseed += sys_jiffies();
+#ifndef LWIP_RAND
+ }
+#endif /* LWIP_RAND */
+}
+
+/*
+ * 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 magic(void) {
+#ifdef LWIP_RAND
+ return LWIP_RAND() + magic_randomseed;
+#else /* LWIP_RAND */
+ return ((u32_t)rand() << 16) + (u32_t)rand() + magic_randomseed;
+#endif /* LWIP_RAND */
+}
+
+/*
+ * magic_random_bytes - Fill a buffer with random bytes.
+ */
+void magic_random_bytes(unsigned char *buf, u32_t buf_len) {
+ u32_t new_rand, n;
+
+ while (buf_len > 0) {
+ new_rand = magic();
+ n = LWIP_MIN(buf_len, sizeof(new_rand));
+ MEMCPY(buf, &new_rand, n);
+ buf += n;
+ buf_len -= n;
+ }
+}
+#endif /* PPP_MD5_RANDM */
+
+/*
+ * Return a new random number between 0 and (2^pow)-1 included.
+ */
+u32_t magic_pow(u8_t pow) {
+ return magic() & ~(~0UL<<pow);
+}
+
+#endif /* PPP_SUPPORT */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/mppe.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/mppe.c
new file mode 100644
index 0000000..331039f
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/mppe.c
@@ -0,0 +1,412 @@
+/*
+ * mppe.c - interface MPPE to the PPP code.
+ *
+ * By Frank Cusack <fcusack@fcusack.com>.
+ * Copyright (c) 2002,2003,2004 Google, Inc.
+ * All rights reserved.
+ *
+ * License:
+ * 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.
+ *
+ * Changelog:
+ * 08/12/05 - Matt Domsch <Matt_Domsch@dell.com>
+ * Only need extra skb padding on transmit, not receive.
+ * 06/18/04 - Matt Domsch <Matt_Domsch@dell.com>, Oleg Makarenko <mole@quadra.ru>
+ * Use Linux kernel 2.6 arc4 and sha1 routines rather than
+ * providing our own.
+ * 2/15/04 - TS: added #include <version.h> and testing for Kernel
+ * version before using
+ * MOD_DEC_USAGE_COUNT/MOD_INC_USAGE_COUNT which are
+ * deprecated in 2.6
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT && MPPE_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#include <string.h>
+
+#include "lwip/err.h"
+
+#include "netif/ppp/ppp_impl.h"
+#include "netif/ppp/ccp.h"
+#include "netif/ppp/mppe.h"
+#include "netif/ppp/pppdebug.h"
+#include "netif/ppp/pppcrypt.h"
+
+#define SHA1_SIGNATURE_SIZE 20
+
+/* ppp_mppe_state.bits definitions */
+#define MPPE_BIT_A 0x80 /* Encryption table were (re)inititalized */
+#define MPPE_BIT_B 0x40 /* MPPC only (not implemented) */
+#define MPPE_BIT_C 0x20 /* MPPC only (not implemented) */
+#define MPPE_BIT_D 0x10 /* This is an encrypted frame */
+
+#define MPPE_BIT_FLUSHED MPPE_BIT_A
+#define MPPE_BIT_ENCRYPTED MPPE_BIT_D
+
+#define MPPE_BITS(p) ((p)[0] & 0xf0)
+#define MPPE_CCOUNT(p) ((((p)[0] & 0x0f) << 8) + (p)[1])
+#define MPPE_CCOUNT_SPACE 0x1000 /* The size of the ccount space */
+
+#define MPPE_OVHD 2 /* MPPE overhead/packet */
+#define SANITY_MAX 1600 /* Max bogon factor we will tolerate */
+
+/*
+ * Perform the MPPE rekey algorithm, from RFC 3078, sec. 7.3.
+ * Well, not what's written there, but rather what they meant.
+ */
+static void mppe_rekey(ppp_mppe_state * state, int initial_key)
+{
+ lwip_sha1_context sha1_ctx;
+ u8_t sha1_digest[SHA1_SIGNATURE_SIZE];
+
+ /*
+ * Key Derivation, from RFC 3078, RFC 3079.
+ * Equivalent to Get_Key() for MS-CHAP as described in RFC 3079.
+ */
+ lwip_sha1_init(&sha1_ctx);
+ lwip_sha1_starts(&sha1_ctx);
+ lwip_sha1_update(&sha1_ctx, state->master_key, state->keylen);
+ lwip_sha1_update(&sha1_ctx, mppe_sha1_pad1, SHA1_PAD_SIZE);
+ lwip_sha1_update(&sha1_ctx, state->session_key, state->keylen);
+ lwip_sha1_update(&sha1_ctx, mppe_sha1_pad2, SHA1_PAD_SIZE);
+ lwip_sha1_finish(&sha1_ctx, sha1_digest);
+ lwip_sha1_free(&sha1_ctx);
+ MEMCPY(state->session_key, sha1_digest, state->keylen);
+
+ if (!initial_key) {
+ lwip_arc4_init(&state->arc4);
+ lwip_arc4_setup(&state->arc4, sha1_digest, state->keylen);
+ lwip_arc4_crypt(&state->arc4, state->session_key, state->keylen);
+ lwip_arc4_free(&state->arc4);
+ }
+ if (state->keylen == 8) {
+ /* See RFC 3078 */
+ state->session_key[0] = 0xd1;
+ state->session_key[1] = 0x26;
+ state->session_key[2] = 0x9e;
+ }
+ lwip_arc4_init(&state->arc4);
+ lwip_arc4_setup(&state->arc4, state->session_key, state->keylen);
+}
+
+/*
+ * Set key, used by MSCHAP before mppe_init() is actually called by CCP so we
+ * don't have to keep multiple copies of keys.
+ */
+void mppe_set_key(ppp_pcb *pcb, ppp_mppe_state *state, u8_t *key) {
+ LWIP_UNUSED_ARG(pcb);
+ MEMCPY(state->master_key, key, MPPE_MAX_KEY_LEN);
+}
+
+/*
+ * Initialize (de)compressor state.
+ */
+void
+mppe_init(ppp_pcb *pcb, ppp_mppe_state *state, u8_t options)
+{
+#if PPP_DEBUG
+ const u8_t *debugstr = (const u8_t*)"mppe_comp_init";
+ if (&pcb->mppe_decomp == state) {
+ debugstr = (const u8_t*)"mppe_decomp_init";
+ }
+#endif /* PPP_DEBUG */
+
+ /* Save keys. */
+ MEMCPY(state->session_key, state->master_key, sizeof(state->master_key));
+
+ if (options & MPPE_OPT_128)
+ state->keylen = 16;
+ else if (options & MPPE_OPT_40)
+ state->keylen = 8;
+ else {
+ PPPDEBUG(LOG_DEBUG, ("%s[%d]: unknown key length\n", debugstr,
+ pcb->netif->num));
+ lcp_close(pcb, "MPPE required but peer negotiation failed");
+ return;
+ }
+ if (options & MPPE_OPT_STATEFUL)
+ state->stateful = 1;
+
+ /* Generate the initial session key. */
+ mppe_rekey(state, 1);
+
+#if PPP_DEBUG
+ {
+ int i;
+ char mkey[sizeof(state->master_key) * 2 + 1];
+ char skey[sizeof(state->session_key) * 2 + 1];
+
+ PPPDEBUG(LOG_DEBUG, ("%s[%d]: initialized with %d-bit %s mode\n",
+ debugstr, pcb->netif->num, (state->keylen == 16) ? 128 : 40,
+ (state->stateful) ? "stateful" : "stateless"));
+
+ for (i = 0; i < (int)sizeof(state->master_key); i++)
+ sprintf(mkey + i * 2, "%02x", state->master_key[i]);
+ for (i = 0; i < (int)sizeof(state->session_key); i++)
+ sprintf(skey + i * 2, "%02x", state->session_key[i]);
+ PPPDEBUG(LOG_DEBUG,
+ ("%s[%d]: keys: master: %s initial session: %s\n",
+ debugstr, pcb->netif->num, mkey, skey));
+ }
+#endif /* PPP_DEBUG */
+
+ /*
+ * Initialize the coherency count. The initial value is not specified
+ * in RFC 3078, but we can make a reasonable assumption that it will
+ * start at 0. Setting it to the max here makes the comp/decomp code
+ * do the right thing (determined through experiment).
+ */
+ state->ccount = MPPE_CCOUNT_SPACE - 1;
+
+ /*
+ * Note that even though we have initialized the key table, we don't
+ * set the FLUSHED bit. This is contrary to RFC 3078, sec. 3.1.
+ */
+ state->bits = MPPE_BIT_ENCRYPTED;
+}
+
+/*
+ * We received a CCP Reset-Request (actually, we are sending a Reset-Ack),
+ * tell the compressor to rekey. Note that we MUST NOT rekey for
+ * every CCP Reset-Request; we only rekey on the next xmit packet.
+ * We might get multiple CCP Reset-Requests if our CCP Reset-Ack is lost.
+ * So, rekeying for every CCP Reset-Request is broken as the peer will not
+ * know how many times we've rekeyed. (If we rekey and THEN get another
+ * CCP Reset-Request, we must rekey again.)
+ */
+void mppe_comp_reset(ppp_pcb *pcb, ppp_mppe_state *state)
+{
+ LWIP_UNUSED_ARG(pcb);
+ state->bits |= MPPE_BIT_FLUSHED;
+}
+
+/*
+ * Compress (encrypt) a packet.
+ * It's strange to call this a compressor, since the output is always
+ * MPPE_OVHD + 2 bytes larger than the input.
+ */
+err_t
+mppe_compress(ppp_pcb *pcb, ppp_mppe_state *state, struct pbuf **pb, u16_t protocol)
+{
+ struct pbuf *n, *np;
+ u8_t *pl;
+ err_t err;
+
+ LWIP_UNUSED_ARG(pcb);
+
+ /* TCP stack requires that we don't change the packet payload, therefore we copy
+ * the whole packet before encryption.
+ */
+ np = pbuf_alloc(PBUF_RAW, MPPE_OVHD + sizeof(protocol) + (*pb)->tot_len, PBUF_POOL);
+ if (!np) {
+ return ERR_MEM;
+ }
+
+ /* Hide MPPE header + protocol */
+ pbuf_header(np, -(s16_t)(MPPE_OVHD + sizeof(protocol)));
+
+ if ((err = pbuf_copy(np, *pb)) != ERR_OK) {
+ pbuf_free(np);
+ return err;
+ }
+
+ /* Reveal MPPE header + protocol */
+ pbuf_header(np, (s16_t)(MPPE_OVHD + sizeof(protocol)));
+
+ *pb = np;
+ pl = (u8_t*)np->payload;
+
+ state->ccount = (state->ccount + 1) % MPPE_CCOUNT_SPACE;
+ PPPDEBUG(LOG_DEBUG, ("mppe_compress[%d]: ccount %d\n", pcb->netif->num, state->ccount));
+ /* FIXME: use PUT* macros */
+ pl[0] = state->ccount>>8;
+ pl[1] = state->ccount;
+
+ if (!state->stateful || /* stateless mode */
+ ((state->ccount & 0xff) == 0xff) || /* "flag" packet */
+ (state->bits & MPPE_BIT_FLUSHED)) { /* CCP Reset-Request */
+ /* We must rekey */
+ if (state->stateful) {
+ PPPDEBUG(LOG_DEBUG, ("mppe_compress[%d]: rekeying\n", pcb->netif->num));
+ }
+ mppe_rekey(state, 0);
+ state->bits |= MPPE_BIT_FLUSHED;
+ }
+ pl[0] |= state->bits;
+ state->bits &= ~MPPE_BIT_FLUSHED; /* reset for next xmit */
+ pl += MPPE_OVHD;
+
+ /* Add protocol */
+ /* FIXME: add PFC support */
+ pl[0] = protocol >> 8;
+ pl[1] = protocol;
+
+ /* Hide MPPE header */
+ pbuf_header(np, -(s16_t)MPPE_OVHD);
+
+ /* Encrypt packet */
+ for (n = np; n != NULL; n = n->next) {
+ lwip_arc4_crypt(&state->arc4, (u8_t*)n->payload, n->len);
+ if (n->tot_len == n->len) {
+ break;
+ }
+ }
+
+ /* Reveal MPPE header */
+ pbuf_header(np, (s16_t)MPPE_OVHD);
+
+ return ERR_OK;
+}
+
+/*
+ * We received a CCP Reset-Ack. Just ignore it.
+ */
+void mppe_decomp_reset(ppp_pcb *pcb, ppp_mppe_state *state)
+{
+ LWIP_UNUSED_ARG(pcb);
+ LWIP_UNUSED_ARG(state);
+ return;
+}
+
+/*
+ * Decompress (decrypt) an MPPE packet.
+ */
+err_t
+mppe_decompress(ppp_pcb *pcb, ppp_mppe_state *state, struct pbuf **pb)
+{
+ struct pbuf *n0 = *pb, *n;
+ u8_t *pl;
+ u16_t ccount;
+ u8_t flushed;
+
+ /* MPPE Header */
+ if (n0->len < MPPE_OVHD) {
+ PPPDEBUG(LOG_DEBUG,
+ ("mppe_decompress[%d]: short pkt (%d)\n",
+ pcb->netif->num, n0->len));
+ state->sanity_errors += 100;
+ goto sanity_error;
+ }
+
+ pl = (u8_t*)n0->payload;
+ flushed = MPPE_BITS(pl) & MPPE_BIT_FLUSHED;
+ ccount = MPPE_CCOUNT(pl);
+ PPPDEBUG(LOG_DEBUG, ("mppe_decompress[%d]: ccount %d\n",
+ pcb->netif->num, ccount));
+
+ /* sanity checks -- terminate with extreme prejudice */
+ if (!(MPPE_BITS(pl) & MPPE_BIT_ENCRYPTED)) {
+ PPPDEBUG(LOG_DEBUG,
+ ("mppe_decompress[%d]: ENCRYPTED bit not set!\n",
+ pcb->netif->num));
+ state->sanity_errors += 100;
+ goto sanity_error;
+ }
+ if (!state->stateful && !flushed) {
+ PPPDEBUG(LOG_DEBUG, ("mppe_decompress[%d]: FLUSHED bit not set in "
+ "stateless mode!\n", pcb->netif->num));
+ state->sanity_errors += 100;
+ goto sanity_error;
+ }
+ if (state->stateful && ((ccount & 0xff) == 0xff) && !flushed) {
+ PPPDEBUG(LOG_DEBUG, ("mppe_decompress[%d]: FLUSHED bit not set on "
+ "flag packet!\n", pcb->netif->num));
+ state->sanity_errors += 100;
+ goto sanity_error;
+ }
+
+ /*
+ * Check the coherency count.
+ */
+
+ if (!state->stateful) {
+ /* Discard late packet */
+ if ((ccount - state->ccount) % MPPE_CCOUNT_SPACE > MPPE_CCOUNT_SPACE / 2) {
+ state->sanity_errors++;
+ goto sanity_error;
+ }
+
+ /* RFC 3078, sec 8.1. Rekey for every packet. */
+ while (state->ccount != ccount) {
+ mppe_rekey(state, 0);
+ state->ccount = (state->ccount + 1) % MPPE_CCOUNT_SPACE;
+ }
+ } else {
+ /* RFC 3078, sec 8.2. */
+ if (!state->discard) {
+ /* normal state */
+ state->ccount = (state->ccount + 1) % MPPE_CCOUNT_SPACE;
+ if (ccount != state->ccount) {
+ /*
+ * (ccount > state->ccount)
+ * Packet loss detected, enter the discard state.
+ * Signal the peer to rekey (by sending a CCP Reset-Request).
+ */
+ state->discard = 1;
+ ccp_resetrequest(pcb);
+ return ERR_BUF;
+ }
+ } else {
+ /* discard state */
+ if (!flushed) {
+ /* ccp.c will be silent (no additional CCP Reset-Requests). */
+ return ERR_BUF;
+ } else {
+ /* Rekey for every missed "flag" packet. */
+ while ((ccount & ~0xff) !=
+ (state->ccount & ~0xff)) {
+ mppe_rekey(state, 0);
+ state->ccount =
+ (state->ccount +
+ 256) % MPPE_CCOUNT_SPACE;
+ }
+
+ /* reset */
+ state->discard = 0;
+ state->ccount = ccount;
+ /*
+ * Another problem with RFC 3078 here. It implies that the
+ * peer need not send a Reset-Ack packet. But RFC 1962
+ * requires it. Hopefully, M$ does send a Reset-Ack; even
+ * though it isn't required for MPPE synchronization, it is
+ * required to reset CCP state.
+ */
+ }
+ }
+ if (flushed)
+ mppe_rekey(state, 0);
+ }
+
+ /* Hide MPPE header */
+ pbuf_header(n0, -(s16_t)(MPPE_OVHD));
+
+ /* Decrypt the packet. */
+ for (n = n0; n != NULL; n = n->next) {
+ lwip_arc4_crypt(&state->arc4, (u8_t*)n->payload, n->len);
+ if (n->tot_len == n->len) {
+ break;
+ }
+ }
+
+ /* good packet credit */
+ state->sanity_errors >>= 1;
+
+ return ERR_OK;
+
+sanity_error:
+ if (state->sanity_errors >= SANITY_MAX) {
+ /*
+ * Take LCP down if the peer is sending too many bogons.
+ * We don't want to do this for a single or just a few
+ * instances since it could just be due to packet corruption.
+ */
+ lcp_close(pcb, "Too many MPPE errors");
+ }
+ return ERR_BUF;
+}
+
+#endif /* PPP_SUPPORT && MPPE_SUPPORT */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/multilink.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/multilink.c
new file mode 100644
index 0000000..62014e8
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/multilink.c
@@ -0,0 +1,609 @@
+/*
+ * multilink.c - support routines for multilink.
+ *
+ * Copyright (c) 2000-2002 Paul Mackerras. 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. The name(s) of the authors of this software must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission.
+ *
+ * 3. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by Paul Mackerras
+ * <paulus@samba.org>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT && defined(HAVE_MULTILINK) /* don't build if not configured for use in lwipopts.h */
+
+/* Multilink support
+ *
+ * Multilink uses Samba TDB (Trivial Database Library), which
+ * we cannot port, because it needs a filesystem.
+ *
+ * We have to choose between doing a memory-shared TDB-clone,
+ * or dropping multilink support at all.
+ */
+
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <netdb.h>
+#include <errno.h>
+#include <signal.h>
+#include <netinet/in.h>
+#include <unistd.h>
+
+#include "netif/ppp/ppp_impl.h"
+
+#include "netif/ppp/fsm.h"
+#include "netif/ppp/lcp.h"
+#include "netif/ppp/tdb.h"
+
+bool endpoint_specified; /* user gave explicit endpoint discriminator */
+char *bundle_id; /* identifier for our bundle */
+char *blinks_id; /* key for the list of links */
+bool doing_multilink; /* multilink was enabled and agreed to */
+bool multilink_master; /* we own the multilink bundle */
+
+extern TDB_CONTEXT *pppdb;
+extern char db_key[];
+
+static void make_bundle_links (int append);
+static void remove_bundle_link (void);
+static void iterate_bundle_links (void (*func) (char *));
+
+static int get_default_epdisc (struct epdisc *);
+static int parse_num (char *str, const char *key, int *valp);
+static int owns_unit (TDB_DATA pid, int unit);
+
+#define set_ip_epdisc(ep, addr) do { \
+ ep->length = 4; \
+ ep->value[0] = addr >> 24; \
+ ep->value[1] = addr >> 16; \
+ ep->value[2] = addr >> 8; \
+ ep->value[3] = addr; \
+} while (0)
+
+#define LOCAL_IP_ADDR(addr) \
+ (((addr) & 0xff000000) == 0x0a000000 /* 10.x.x.x */ \
+ || ((addr) & 0xfff00000) == 0xac100000 /* 172.16.x.x */ \
+ || ((addr) & 0xffff0000) == 0xc0a80000) /* 192.168.x.x */
+
+#define process_exists(n) (kill((n), 0) == 0 || errno != ESRCH)
+
+void
+mp_check_options()
+{
+ lcp_options *wo = &lcp_wantoptions[0];
+ lcp_options *ao = &lcp_allowoptions[0];
+
+ doing_multilink = 0;
+ if (!multilink)
+ return;
+ /* if we're doing multilink, we have to negotiate MRRU */
+ if (!wo->neg_mrru) {
+ /* mrru not specified, default to mru */
+ wo->mrru = wo->mru;
+ wo->neg_mrru = 1;
+ }
+ ao->mrru = ao->mru;
+ ao->neg_mrru = 1;
+
+ if (!wo->neg_endpoint && !noendpoint) {
+ /* get a default endpoint value */
+ wo->neg_endpoint = get_default_epdisc(&wo->endpoint);
+ }
+}
+
+/*
+ * Make a new bundle or join us to an existing bundle
+ * if we are doing multilink.
+ */
+int
+mp_join_bundle()
+{
+ lcp_options *go = &lcp_gotoptions[0];
+ lcp_options *ho = &lcp_hisoptions[0];
+ lcp_options *ao = &lcp_allowoptions[0];
+ int unit, pppd_pid;
+ int l, mtu;
+ char *p;
+ TDB_DATA key, pid, rec;
+
+ if (doing_multilink) {
+ /* have previously joined a bundle */
+ if (!go->neg_mrru || !ho->neg_mrru) {
+ notice("oops, didn't get multilink on renegotiation");
+ lcp_close(pcb, "multilink required");
+ return 0;
+ }
+ /* XXX should check the peer_authname and ho->endpoint
+ are the same as previously */
+ return 0;
+ }
+
+ if (!go->neg_mrru || !ho->neg_mrru) {
+ /* not doing multilink */
+ if (go->neg_mrru)
+ notice("oops, multilink negotiated only for receive");
+ mtu = ho->neg_mru? ho->mru: PPP_MRU;
+ if (mtu > ao->mru)
+ mtu = ao->mru;
+ if (demand) {
+ /* already have a bundle */
+ cfg_bundle(0, 0, 0, 0);
+ netif_set_mtu(pcb, mtu);
+ return 0;
+ }
+ make_new_bundle(0, 0, 0, 0);
+ set_ifunit(1);
+ netif_set_mtu(pcb, mtu);
+ return 0;
+ }
+
+ doing_multilink = 1;
+
+ /*
+ * Find the appropriate bundle or join a new one.
+ * First we make up a name for the bundle.
+ * The length estimate is worst-case assuming every
+ * character has to be quoted.
+ */
+ l = 4 * strlen(peer_authname) + 10;
+ if (ho->neg_endpoint)
+ l += 3 * ho->endpoint.length + 8;
+ if (bundle_name)
+ l += 3 * strlen(bundle_name) + 2;
+ bundle_id = malloc(l);
+ if (bundle_id == 0)
+ novm("bundle identifier");
+
+ p = bundle_id;
+ p += slprintf(p, l-1, "BUNDLE=\"%q\"", peer_authname);
+ if (ho->neg_endpoint || bundle_name)
+ *p++ = '/';
+ if (ho->neg_endpoint)
+ p += slprintf(p, bundle_id+l-p, "%s",
+ epdisc_to_str(&ho->endpoint));
+ if (bundle_name)
+ p += slprintf(p, bundle_id+l-p, "/%v", bundle_name);
+
+ /* Make the key for the list of links belonging to the bundle */
+ l = p - bundle_id;
+ blinks_id = malloc(l + 7);
+ if (blinks_id == NULL)
+ novm("bundle links key");
+ slprintf(blinks_id, l + 7, "BUNDLE_LINKS=%s", bundle_id + 7);
+
+ /*
+ * For demand mode, we only need to configure the bundle
+ * and attach the link.
+ */
+ mtu = LWIP_MIN(ho->mrru, ao->mru);
+ if (demand) {
+ cfg_bundle(go->mrru, ho->mrru, go->neg_ssnhf, ho->neg_ssnhf);
+ netif_set_mtu(pcb, mtu);
+ script_setenv("BUNDLE", bundle_id + 7, 1);
+ return 0;
+ }
+
+ /*
+ * Check if the bundle ID is already in the database.
+ */
+ unit = -1;
+ lock_db();
+ key.dptr = bundle_id;
+ key.dsize = p - bundle_id;
+ pid = tdb_fetch(pppdb, key);
+ if (pid.dptr != NULL) {
+ /* bundle ID exists, see if the pppd record exists */
+ rec = tdb_fetch(pppdb, pid);
+ if (rec.dptr != NULL && rec.dsize > 0) {
+ /* make sure the string is null-terminated */
+ rec.dptr[rec.dsize-1] = 0;
+ /* parse the interface number */
+ parse_num(rec.dptr, "IFNAME=ppp", &unit);
+ /* check the pid value */
+ if (!parse_num(rec.dptr, "PPPD_PID=", &pppd_pid)
+ || !process_exists(pppd_pid)
+ || !owns_unit(pid, unit))
+ unit = -1;
+ free(rec.dptr);
+ }
+ free(pid.dptr);
+ }
+
+ if (unit >= 0) {
+ /* attach to existing unit */
+ if (bundle_attach(unit)) {
+ set_ifunit(0);
+ script_setenv("BUNDLE", bundle_id + 7, 0);
+ make_bundle_links(1);
+ unlock_db();
+ info("Link attached to %s", ifname);
+ return 1;
+ }
+ /* attach failed because bundle doesn't exist */
+ }
+
+ /* we have to make a new bundle */
+ make_new_bundle(go->mrru, ho->mrru, go->neg_ssnhf, ho->neg_ssnhf);
+ set_ifunit(1);
+ netif_set_mtu(pcb, mtu);
+ script_setenv("BUNDLE", bundle_id + 7, 1);
+ make_bundle_links(pcb);
+ unlock_db();
+ info("New bundle %s created", ifname);
+ multilink_master = 1;
+ return 0;
+}
+
+void mp_exit_bundle()
+{
+ lock_db();
+ remove_bundle_link();
+ unlock_db();
+}
+
+static void sendhup(char *str)
+{
+ int pid;
+
+ if (parse_num(str, "PPPD_PID=", &pid) && pid != getpid()) {
+ if (debug)
+ dbglog("sending SIGHUP to process %d", pid);
+ kill(pid, SIGHUP);
+ }
+}
+
+void mp_bundle_terminated()
+{
+ TDB_DATA key;
+
+ bundle_terminating = 1;
+ upper_layers_down(pcb);
+ notice("Connection terminated.");
+#if PPP_STATS_SUPPORT
+ print_link_stats();
+#endif /* PPP_STATS_SUPPORT */
+ if (!demand) {
+ remove_pidfiles();
+ script_unsetenv("IFNAME");
+ }
+
+ lock_db();
+ destroy_bundle();
+ iterate_bundle_links(sendhup);
+ key.dptr = blinks_id;
+ key.dsize = strlen(blinks_id);
+ tdb_delete(pppdb, key);
+ unlock_db();
+
+ new_phase(PPP_PHASE_DEAD);
+
+ doing_multilink = 0;
+ multilink_master = 0;
+}
+
+static void make_bundle_links(int append)
+{
+ TDB_DATA key, rec;
+ char *p;
+ char entry[32];
+ int l;
+
+ key.dptr = blinks_id;
+ key.dsize = strlen(blinks_id);
+ slprintf(entry, sizeof(entry), "%s;", db_key);
+ p = entry;
+ if (append) {
+ rec = tdb_fetch(pppdb, key);
+ if (rec.dptr != NULL && rec.dsize > 0) {
+ rec.dptr[rec.dsize-1] = 0;
+ if (strstr(rec.dptr, db_key) != NULL) {
+ /* already in there? strange */
+ warn("link entry already exists in tdb");
+ return;
+ }
+ l = rec.dsize + strlen(entry);
+ p = malloc(l);
+ if (p == NULL)
+ novm("bundle link list");
+ slprintf(p, l, "%s%s", rec.dptr, entry);
+ } else {
+ warn("bundle link list not found");
+ }
+ if (rec.dptr != NULL)
+ free(rec.dptr);
+ }
+ rec.dptr = p;
+ rec.dsize = strlen(p) + 1;
+ if (tdb_store(pppdb, key, rec, TDB_REPLACE))
+ error("couldn't %s bundle link list",
+ append? "update": "create");
+ if (p != entry)
+ free(p);
+}
+
+static void remove_bundle_link()
+{
+ TDB_DATA key, rec;
+ char entry[32];
+ char *p, *q;
+ int l;
+
+ key.dptr = blinks_id;
+ key.dsize = strlen(blinks_id);
+ slprintf(entry, sizeof(entry), "%s;", db_key);
+
+ rec = tdb_fetch(pppdb, key);
+ if (rec.dptr == NULL || rec.dsize <= 0) {
+ if (rec.dptr != NULL)
+ free(rec.dptr);
+ return;
+ }
+ rec.dptr[rec.dsize-1] = 0;
+ p = strstr(rec.dptr, entry);
+ if (p != NULL) {
+ q = p + strlen(entry);
+ l = strlen(q) + 1;
+ memmove(p, q, l);
+ rec.dsize = p - rec.dptr + l;
+ if (tdb_store(pppdb, key, rec, TDB_REPLACE))
+ error("couldn't update bundle link list (removal)");
+ }
+ free(rec.dptr);
+}
+
+static void iterate_bundle_links(void (*func)(char *))
+{
+ TDB_DATA key, rec, pp;
+ char *p, *q;
+
+ key.dptr = blinks_id;
+ key.dsize = strlen(blinks_id);
+ rec = tdb_fetch(pppdb, key);
+ if (rec.dptr == NULL || rec.dsize <= 0) {
+ error("bundle link list not found (iterating list)");
+ if (rec.dptr != NULL)
+ free(rec.dptr);
+ return;
+ }
+ p = rec.dptr;
+ p[rec.dsize-1] = 0;
+ while ((q = strchr(p, ';')) != NULL) {
+ *q = 0;
+ key.dptr = p;
+ key.dsize = q - p;
+ pp = tdb_fetch(pppdb, key);
+ if (pp.dptr != NULL && pp.dsize > 0) {
+ pp.dptr[pp.dsize-1] = 0;
+ func(pp.dptr);
+ }
+ if (pp.dptr != NULL)
+ free(pp.dptr);
+ p = q + 1;
+ }
+ free(rec.dptr);
+}
+
+static int
+parse_num(str, key, valp)
+ char *str;
+ const char *key;
+ int *valp;
+{
+ char *p, *endp;
+ int i;
+
+ p = strstr(str, key);
+ if (p != 0) {
+ p += strlen(key);
+ i = strtol(p, &endp, 10);
+ if (endp != p && (*endp == 0 || *endp == ';')) {
+ *valp = i;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Check whether the pppd identified by `key' still owns ppp unit `unit'.
+ */
+static int
+owns_unit(key, unit)
+ TDB_DATA key;
+ int unit;
+{
+ char ifkey[32];
+ TDB_DATA kd, vd;
+ int ret = 0;
+
+ slprintf(ifkey, sizeof(ifkey), "IFNAME=ppp%d", unit);
+ kd.dptr = ifkey;
+ kd.dsize = strlen(ifkey);
+ vd = tdb_fetch(pppdb, kd);
+ if (vd.dptr != NULL) {
+ ret = vd.dsize == key.dsize
+ && memcmp(vd.dptr, key.dptr, vd.dsize) == 0;
+ free(vd.dptr);
+ }
+ return ret;
+}
+
+static int
+get_default_epdisc(ep)
+ struct epdisc *ep;
+{
+ char *p;
+ struct hostent *hp;
+ u32_t addr;
+
+ /* First try for an ethernet MAC address */
+ p = get_first_ethernet();
+ if (p != 0 && get_if_hwaddr(ep->value, p) >= 0) {
+ ep->class = EPD_MAC;
+ ep->length = 6;
+ return 1;
+ }
+
+ /* see if our hostname corresponds to a reasonable IP address */
+ hp = gethostbyname(hostname);
+ if (hp != NULL) {
+ addr = *(u32_t *)hp->h_addr;
+ if (!bad_ip_adrs(addr)) {
+ addr = lwip_ntohl(addr);
+ if (!LOCAL_IP_ADDR(addr)) {
+ ep->class = EPD_IP;
+ set_ip_epdisc(ep, addr);
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * epdisc_to_str - make a printable string from an endpoint discriminator.
+ */
+
+static char *endp_class_names[] = {
+ "null", "local", "IP", "MAC", "magic", "phone"
+};
+
+char *
+epdisc_to_str(ep)
+ struct epdisc *ep;
+{
+ static char str[MAX_ENDP_LEN*3+8];
+ u_char *p = ep->value;
+ int i, mask = 0;
+ char *q, c, c2;
+
+ if (ep->class == EPD_NULL && ep->length == 0)
+ return "null";
+ if (ep->class == EPD_IP && ep->length == 4) {
+ u32_t addr;
+
+ GETLONG(addr, p);
+ slprintf(str, sizeof(str), "IP:%I", lwip_htonl(addr));
+ return str;
+ }
+
+ c = ':';
+ c2 = '.';
+ if (ep->class == EPD_MAC && ep->length == 6)
+ c2 = ':';
+ else if (ep->class == EPD_MAGIC && (ep->length % 4) == 0)
+ mask = 3;
+ q = str;
+ if (ep->class <= EPD_PHONENUM)
+ q += slprintf(q, sizeof(str)-1, "%s",
+ endp_class_names[ep->class]);
+ else
+ q += slprintf(q, sizeof(str)-1, "%d", ep->class);
+ c = ':';
+ for (i = 0; i < ep->length && i < MAX_ENDP_LEN; ++i) {
+ if ((i & mask) == 0) {
+ *q++ = c;
+ c = c2;
+ }
+ q += slprintf(q, str + sizeof(str) - q, "%.2x", ep->value[i]);
+ }
+ return str;
+}
+
+static int hexc_val(int c)
+{
+ if (c >= 'a')
+ return c - 'a' + 10;
+ if (c >= 'A')
+ return c - 'A' + 10;
+ return c - '0';
+}
+
+int
+str_to_epdisc(ep, str)
+ struct epdisc *ep;
+ char *str;
+{
+ int i, l;
+ char *p, *endp;
+
+ for (i = EPD_NULL; i <= EPD_PHONENUM; ++i) {
+ int sl = strlen(endp_class_names[i]);
+ if (strncasecmp(str, endp_class_names[i], sl) == 0) {
+ str += sl;
+ break;
+ }
+ }
+ if (i > EPD_PHONENUM) {
+ /* not a class name, try a decimal class number */
+ i = strtol(str, &endp, 10);
+ if (endp == str)
+ return 0; /* can't parse class number */
+ str = endp;
+ }
+ ep->class = i;
+ if (*str == 0) {
+ ep->length = 0;
+ return 1;
+ }
+ if (*str != ':' && *str != '.')
+ return 0;
+ ++str;
+
+ if (i == EPD_IP) {
+ u32_t addr;
+ i = parse_dotted_ip(str, &addr);
+ if (i == 0 || str[i] != 0)
+ return 0;
+ set_ip_epdisc(ep, addr);
+ return 1;
+ }
+ if (i == EPD_MAC && get_if_hwaddr(ep->value, str) >= 0) {
+ ep->length = 6;
+ return 1;
+ }
+
+ p = str;
+ for (l = 0; l < MAX_ENDP_LEN; ++l) {
+ if (*str == 0)
+ break;
+ if (p <= str)
+ for (p = str; isxdigit(*p); ++p)
+ ;
+ i = p - str;
+ if (i == 0)
+ return 0;
+ ep->value[l] = hexc_val(*str++);
+ if ((i & 1) == 0)
+ ep->value[l] = (ep->value[l] << 4) + hexc_val(*str++);
+ if (*str == ':' || *str == '.')
+ ++str;
+ }
+ if (*str != 0 || (ep->class == EPD_MAC && l != 6))
+ return 0;
+ ep->length = l;
+ return 1;
+}
+
+#endif /* PPP_SUPPORT && HAVE_MULTILINK */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/polarssl/README b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/polarssl/README
new file mode 100644
index 0000000..3fdf159
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/polarssl/README
@@ -0,0 +1,22 @@
+About PolarSSL files into lwIP PPP support
+------------------------------------------
+
+This folder contains some files fetched from the latest BSD release of
+the PolarSSL project (PolarSSL 0.10.1-bsd) for ciphers and encryption
+methods we need for lwIP PPP support.
+
+The PolarSSL files were cleaned to contain only the necessary struct
+fields and functions needed for lwIP.
+
+The PolarSSL API was not changed at all, so if you are already using
+PolarSSL you can choose to skip the compilation of the included PolarSSL
+library into lwIP.
+
+If you are not using the embedded copy you must include external
+libraries into your arch/cc.h port file.
+
+Beware of the stack requirements which can be a lot larger if you are not
+using our cleaned PolarSSL library.
+
+
+PolarSSL project website: http://polarssl.org/
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/polarssl/arc4.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/polarssl/arc4.c
new file mode 100644
index 0000000..6e17ec4
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/polarssl/arc4.c
@@ -0,0 +1,101 @@
+/*
+ * An implementation of the ARCFOUR algorithm
+ *
+ * Based on XySSL: Copyright (C) 2006-2008 Christophe Devine
+ *
+ * Copyright (C) 2009 Paul Bakker <polarssl_maintainer at polarssl dot org>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the names of PolarSSL or XySSL 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 COPYRIGHT HOLDERS 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 COPYRIGHT
+ * OWNER 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.
+ */
+/*
+ * The ARCFOUR algorithm was publicly disclosed on 94/09.
+ *
+ * http://groups.google.com/group/sci.crypt/msg/10a300c9d21afca0
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT && LWIP_INCLUDED_POLARSSL_ARC4
+
+#include "netif/ppp/polarssl/arc4.h"
+/*
+ * ARC4 key schedule
+ */
+void arc4_setup( arc4_context *ctx, unsigned char *key, int keylen )
+{
+ int i, j, k, a;
+ unsigned char *m;
+
+ ctx->x = 0;
+ ctx->y = 0;
+ m = ctx->m;
+
+ for( i = 0; i < 256; i++ )
+ m[i] = (unsigned char) i;
+
+ j = k = 0;
+
+ for( i = 0; i < 256; i++, k++ )
+ {
+ if( k >= keylen ) k = 0;
+
+ a = m[i];
+ j = ( j + a + key[k] ) & 0xFF;
+ m[i] = m[j];
+ m[j] = (unsigned char) a;
+ }
+}
+
+/*
+ * ARC4 cipher function
+ */
+void arc4_crypt( arc4_context *ctx, unsigned char *buf, int buflen )
+{
+ int i, x, y, a, b;
+ unsigned char *m;
+
+ x = ctx->x;
+ y = ctx->y;
+ m = ctx->m;
+
+ for( i = 0; i < buflen; i++ )
+ {
+ x = ( x + 1 ) & 0xFF; a = m[x];
+ y = ( y + a ) & 0xFF; b = m[y];
+
+ m[x] = (unsigned char) b;
+ m[y] = (unsigned char) a;
+
+ buf[i] = (unsigned char)
+ ( buf[i] ^ m[(unsigned char)( a + b )] );
+ }
+
+ ctx->x = x;
+ ctx->y = y;
+}
+
+#endif /* PPP_SUPPORT && LWIP_INCLUDED_POLARSSL_DES */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/polarssl/des.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/polarssl/des.c
new file mode 100644
index 0000000..9a89d00
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/polarssl/des.c
@@ -0,0 +1,422 @@
+/*
+ * FIPS-46-3 compliant Triple-DES implementation
+ *
+ * Based on XySSL: Copyright (C) 2006-2008 Christophe Devine
+ *
+ * Copyright (C) 2009 Paul Bakker <polarssl_maintainer at polarssl dot org>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the names of PolarSSL or XySSL 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 COPYRIGHT HOLDERS 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 COPYRIGHT
+ * OWNER 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.
+ */
+/*
+ * DES, on which TDES is based, was originally designed by Horst Feistel
+ * at IBM in 1974, and was adopted as a standard by NIST (formerly NBS).
+ *
+ * http://csrc.nist.gov/publications/fips/fips46-3/fips46-3.pdf
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT && LWIP_INCLUDED_POLARSSL_DES
+
+#include "netif/ppp/polarssl/des.h"
+
+/*
+ * 32-bit integer manipulation macros (big endian)
+ */
+#ifndef GET_ULONG_BE
+#define GET_ULONG_BE(n,b,i) \
+{ \
+ (n) = ( (unsigned long) (b)[(i) ] << 24 ) \
+ | ( (unsigned long) (b)[(i) + 1] << 16 ) \
+ | ( (unsigned long) (b)[(i) + 2] << 8 ) \
+ | ( (unsigned long) (b)[(i) + 3] ); \
+}
+#endif
+
+#ifndef PUT_ULONG_BE
+#define PUT_ULONG_BE(n,b,i) \
+{ \
+ (b)[(i) ] = (unsigned char) ( (n) >> 24 ); \
+ (b)[(i) + 1] = (unsigned char) ( (n) >> 16 ); \
+ (b)[(i) + 2] = (unsigned char) ( (n) >> 8 ); \
+ (b)[(i) + 3] = (unsigned char) ( (n) ); \
+}
+#endif
+
+/*
+ * Expanded DES S-boxes
+ */
+static const unsigned long SB1[64] =
+{
+ 0x01010400, 0x00000000, 0x00010000, 0x01010404,
+ 0x01010004, 0x00010404, 0x00000004, 0x00010000,
+ 0x00000400, 0x01010400, 0x01010404, 0x00000400,
+ 0x01000404, 0x01010004, 0x01000000, 0x00000004,
+ 0x00000404, 0x01000400, 0x01000400, 0x00010400,
+ 0x00010400, 0x01010000, 0x01010000, 0x01000404,
+ 0x00010004, 0x01000004, 0x01000004, 0x00010004,
+ 0x00000000, 0x00000404, 0x00010404, 0x01000000,
+ 0x00010000, 0x01010404, 0x00000004, 0x01010000,
+ 0x01010400, 0x01000000, 0x01000000, 0x00000400,
+ 0x01010004, 0x00010000, 0x00010400, 0x01000004,
+ 0x00000400, 0x00000004, 0x01000404, 0x00010404,
+ 0x01010404, 0x00010004, 0x01010000, 0x01000404,
+ 0x01000004, 0x00000404, 0x00010404, 0x01010400,
+ 0x00000404, 0x01000400, 0x01000400, 0x00000000,
+ 0x00010004, 0x00010400, 0x00000000, 0x01010004
+};
+
+static const unsigned long SB2[64] =
+{
+ 0x80108020, 0x80008000, 0x00008000, 0x00108020,
+ 0x00100000, 0x00000020, 0x80100020, 0x80008020,
+ 0x80000020, 0x80108020, 0x80108000, 0x80000000,
+ 0x80008000, 0x00100000, 0x00000020, 0x80100020,
+ 0x00108000, 0x00100020, 0x80008020, 0x00000000,
+ 0x80000000, 0x00008000, 0x00108020, 0x80100000,
+ 0x00100020, 0x80000020, 0x00000000, 0x00108000,
+ 0x00008020, 0x80108000, 0x80100000, 0x00008020,
+ 0x00000000, 0x00108020, 0x80100020, 0x00100000,
+ 0x80008020, 0x80100000, 0x80108000, 0x00008000,
+ 0x80100000, 0x80008000, 0x00000020, 0x80108020,
+ 0x00108020, 0x00000020, 0x00008000, 0x80000000,
+ 0x00008020, 0x80108000, 0x00100000, 0x80000020,
+ 0x00100020, 0x80008020, 0x80000020, 0x00100020,
+ 0x00108000, 0x00000000, 0x80008000, 0x00008020,
+ 0x80000000, 0x80100020, 0x80108020, 0x00108000
+};
+
+static const unsigned long SB3[64] =
+{
+ 0x00000208, 0x08020200, 0x00000000, 0x08020008,
+ 0x08000200, 0x00000000, 0x00020208, 0x08000200,
+ 0x00020008, 0x08000008, 0x08000008, 0x00020000,
+ 0x08020208, 0x00020008, 0x08020000, 0x00000208,
+ 0x08000000, 0x00000008, 0x08020200, 0x00000200,
+ 0x00020200, 0x08020000, 0x08020008, 0x00020208,
+ 0x08000208, 0x00020200, 0x00020000, 0x08000208,
+ 0x00000008, 0x08020208, 0x00000200, 0x08000000,
+ 0x08020200, 0x08000000, 0x00020008, 0x00000208,
+ 0x00020000, 0x08020200, 0x08000200, 0x00000000,
+ 0x00000200, 0x00020008, 0x08020208, 0x08000200,
+ 0x08000008, 0x00000200, 0x00000000, 0x08020008,
+ 0x08000208, 0x00020000, 0x08000000, 0x08020208,
+ 0x00000008, 0x00020208, 0x00020200, 0x08000008,
+ 0x08020000, 0x08000208, 0x00000208, 0x08020000,
+ 0x00020208, 0x00000008, 0x08020008, 0x00020200
+};
+
+static const unsigned long SB4[64] =
+{
+ 0x00802001, 0x00002081, 0x00002081, 0x00000080,
+ 0x00802080, 0x00800081, 0x00800001, 0x00002001,
+ 0x00000000, 0x00802000, 0x00802000, 0x00802081,
+ 0x00000081, 0x00000000, 0x00800080, 0x00800001,
+ 0x00000001, 0x00002000, 0x00800000, 0x00802001,
+ 0x00000080, 0x00800000, 0x00002001, 0x00002080,
+ 0x00800081, 0x00000001, 0x00002080, 0x00800080,
+ 0x00002000, 0x00802080, 0x00802081, 0x00000081,
+ 0x00800080, 0x00800001, 0x00802000, 0x00802081,
+ 0x00000081, 0x00000000, 0x00000000, 0x00802000,
+ 0x00002080, 0x00800080, 0x00800081, 0x00000001,
+ 0x00802001, 0x00002081, 0x00002081, 0x00000080,
+ 0x00802081, 0x00000081, 0x00000001, 0x00002000,
+ 0x00800001, 0x00002001, 0x00802080, 0x00800081,
+ 0x00002001, 0x00002080, 0x00800000, 0x00802001,
+ 0x00000080, 0x00800000, 0x00002000, 0x00802080
+};
+
+static const unsigned long SB5[64] =
+{
+ 0x00000100, 0x02080100, 0x02080000, 0x42000100,
+ 0x00080000, 0x00000100, 0x40000000, 0x02080000,
+ 0x40080100, 0x00080000, 0x02000100, 0x40080100,
+ 0x42000100, 0x42080000, 0x00080100, 0x40000000,
+ 0x02000000, 0x40080000, 0x40080000, 0x00000000,
+ 0x40000100, 0x42080100, 0x42080100, 0x02000100,
+ 0x42080000, 0x40000100, 0x00000000, 0x42000000,
+ 0x02080100, 0x02000000, 0x42000000, 0x00080100,
+ 0x00080000, 0x42000100, 0x00000100, 0x02000000,
+ 0x40000000, 0x02080000, 0x42000100, 0x40080100,
+ 0x02000100, 0x40000000, 0x42080000, 0x02080100,
+ 0x40080100, 0x00000100, 0x02000000, 0x42080000,
+ 0x42080100, 0x00080100, 0x42000000, 0x42080100,
+ 0x02080000, 0x00000000, 0x40080000, 0x42000000,
+ 0x00080100, 0x02000100, 0x40000100, 0x00080000,
+ 0x00000000, 0x40080000, 0x02080100, 0x40000100
+};
+
+static const unsigned long SB6[64] =
+{
+ 0x20000010, 0x20400000, 0x00004000, 0x20404010,
+ 0x20400000, 0x00000010, 0x20404010, 0x00400000,
+ 0x20004000, 0x00404010, 0x00400000, 0x20000010,
+ 0x00400010, 0x20004000, 0x20000000, 0x00004010,
+ 0x00000000, 0x00400010, 0x20004010, 0x00004000,
+ 0x00404000, 0x20004010, 0x00000010, 0x20400010,
+ 0x20400010, 0x00000000, 0x00404010, 0x20404000,
+ 0x00004010, 0x00404000, 0x20404000, 0x20000000,
+ 0x20004000, 0x00000010, 0x20400010, 0x00404000,
+ 0x20404010, 0x00400000, 0x00004010, 0x20000010,
+ 0x00400000, 0x20004000, 0x20000000, 0x00004010,
+ 0x20000010, 0x20404010, 0x00404000, 0x20400000,
+ 0x00404010, 0x20404000, 0x00000000, 0x20400010,
+ 0x00000010, 0x00004000, 0x20400000, 0x00404010,
+ 0x00004000, 0x00400010, 0x20004010, 0x00000000,
+ 0x20404000, 0x20000000, 0x00400010, 0x20004010
+};
+
+static const unsigned long SB7[64] =
+{
+ 0x00200000, 0x04200002, 0x04000802, 0x00000000,
+ 0x00000800, 0x04000802, 0x00200802, 0x04200800,
+ 0x04200802, 0x00200000, 0x00000000, 0x04000002,
+ 0x00000002, 0x04000000, 0x04200002, 0x00000802,
+ 0x04000800, 0x00200802, 0x00200002, 0x04000800,
+ 0x04000002, 0x04200000, 0x04200800, 0x00200002,
+ 0x04200000, 0x00000800, 0x00000802, 0x04200802,
+ 0x00200800, 0x00000002, 0x04000000, 0x00200800,
+ 0x04000000, 0x00200800, 0x00200000, 0x04000802,
+ 0x04000802, 0x04200002, 0x04200002, 0x00000002,
+ 0x00200002, 0x04000000, 0x04000800, 0x00200000,
+ 0x04200800, 0x00000802, 0x00200802, 0x04200800,
+ 0x00000802, 0x04000002, 0x04200802, 0x04200000,
+ 0x00200800, 0x00000000, 0x00000002, 0x04200802,
+ 0x00000000, 0x00200802, 0x04200000, 0x00000800,
+ 0x04000002, 0x04000800, 0x00000800, 0x00200002
+};
+
+static const unsigned long SB8[64] =
+{
+ 0x10001040, 0x00001000, 0x00040000, 0x10041040,
+ 0x10000000, 0x10001040, 0x00000040, 0x10000000,
+ 0x00040040, 0x10040000, 0x10041040, 0x00041000,
+ 0x10041000, 0x00041040, 0x00001000, 0x00000040,
+ 0x10040000, 0x10000040, 0x10001000, 0x00001040,
+ 0x00041000, 0x00040040, 0x10040040, 0x10041000,
+ 0x00001040, 0x00000000, 0x00000000, 0x10040040,
+ 0x10000040, 0x10001000, 0x00041040, 0x00040000,
+ 0x00041040, 0x00040000, 0x10041000, 0x00001000,
+ 0x00000040, 0x10040040, 0x00001000, 0x00041040,
+ 0x10001000, 0x00000040, 0x10000040, 0x10040000,
+ 0x10040040, 0x10000000, 0x00040000, 0x10001040,
+ 0x00000000, 0x10041040, 0x00040040, 0x10000040,
+ 0x10040000, 0x10001000, 0x10001040, 0x00000000,
+ 0x10041040, 0x00041000, 0x00041000, 0x00001040,
+ 0x00001040, 0x00040040, 0x10000000, 0x10041000
+};
+
+/*
+ * PC1: left and right halves bit-swap
+ */
+static const unsigned long LHs[16] =
+{
+ 0x00000000, 0x00000001, 0x00000100, 0x00000101,
+ 0x00010000, 0x00010001, 0x00010100, 0x00010101,
+ 0x01000000, 0x01000001, 0x01000100, 0x01000101,
+ 0x01010000, 0x01010001, 0x01010100, 0x01010101
+};
+
+static const unsigned long RHs[16] =
+{
+ 0x00000000, 0x01000000, 0x00010000, 0x01010000,
+ 0x00000100, 0x01000100, 0x00010100, 0x01010100,
+ 0x00000001, 0x01000001, 0x00010001, 0x01010001,
+ 0x00000101, 0x01000101, 0x00010101, 0x01010101,
+};
+
+/*
+ * Initial Permutation macro
+ */
+#define DES_IP(X,Y) \
+{ \
+ T = ((X >> 4) ^ Y) & 0x0F0F0F0F; Y ^= T; X ^= (T << 4); \
+ T = ((X >> 16) ^ Y) & 0x0000FFFF; Y ^= T; X ^= (T << 16); \
+ T = ((Y >> 2) ^ X) & 0x33333333; X ^= T; Y ^= (T << 2); \
+ T = ((Y >> 8) ^ X) & 0x00FF00FF; X ^= T; Y ^= (T << 8); \
+ Y = ((Y << 1) | (Y >> 31)) & 0xFFFFFFFF; \
+ T = (X ^ Y) & 0xAAAAAAAA; Y ^= T; X ^= T; \
+ X = ((X << 1) | (X >> 31)) & 0xFFFFFFFF; \
+}
+
+/*
+ * Final Permutation macro
+ */
+#define DES_FP(X,Y) \
+{ \
+ X = ((X << 31) | (X >> 1)) & 0xFFFFFFFF; \
+ T = (X ^ Y) & 0xAAAAAAAA; X ^= T; Y ^= T; \
+ Y = ((Y << 31) | (Y >> 1)) & 0xFFFFFFFF; \
+ T = ((Y >> 8) ^ X) & 0x00FF00FF; X ^= T; Y ^= (T << 8); \
+ T = ((Y >> 2) ^ X) & 0x33333333; X ^= T; Y ^= (T << 2); \
+ T = ((X >> 16) ^ Y) & 0x0000FFFF; Y ^= T; X ^= (T << 16); \
+ T = ((X >> 4) ^ Y) & 0x0F0F0F0F; Y ^= T; X ^= (T << 4); \
+}
+
+/*
+ * DES round macro
+ */
+#define DES_ROUND(X,Y) \
+{ \
+ T = *SK++ ^ X; \
+ Y ^= SB8[ (T ) & 0x3F ] ^ \
+ SB6[ (T >> 8) & 0x3F ] ^ \
+ SB4[ (T >> 16) & 0x3F ] ^ \
+ SB2[ (T >> 24) & 0x3F ]; \
+ \
+ T = *SK++ ^ ((X << 28) | (X >> 4)); \
+ Y ^= SB7[ (T ) & 0x3F ] ^ \
+ SB5[ (T >> 8) & 0x3F ] ^ \
+ SB3[ (T >> 16) & 0x3F ] ^ \
+ SB1[ (T >> 24) & 0x3F ]; \
+}
+
+#define SWAP(a,b) { unsigned long t = a; a = b; b = t; t = 0; }
+
+static void des_setkey( unsigned long SK[32], unsigned char key[8] )
+{
+ int i;
+ unsigned long X, Y, T;
+
+ GET_ULONG_BE( X, key, 0 );
+ GET_ULONG_BE( Y, key, 4 );
+
+ /*
+ * Permuted Choice 1
+ */
+ T = ((Y >> 4) ^ X) & 0x0F0F0F0F; X ^= T; Y ^= (T << 4);
+ T = ((Y ) ^ X) & 0x10101010; X ^= T; Y ^= (T );
+
+ X = (LHs[ (X ) & 0xF] << 3) | (LHs[ (X >> 8) & 0xF ] << 2)
+ | (LHs[ (X >> 16) & 0xF] << 1) | (LHs[ (X >> 24) & 0xF ] )
+ | (LHs[ (X >> 5) & 0xF] << 7) | (LHs[ (X >> 13) & 0xF ] << 6)
+ | (LHs[ (X >> 21) & 0xF] << 5) | (LHs[ (X >> 29) & 0xF ] << 4);
+
+ Y = (RHs[ (Y >> 1) & 0xF] << 3) | (RHs[ (Y >> 9) & 0xF ] << 2)
+ | (RHs[ (Y >> 17) & 0xF] << 1) | (RHs[ (Y >> 25) & 0xF ] )
+ | (RHs[ (Y >> 4) & 0xF] << 7) | (RHs[ (Y >> 12) & 0xF ] << 6)
+ | (RHs[ (Y >> 20) & 0xF] << 5) | (RHs[ (Y >> 28) & 0xF ] << 4);
+
+ X &= 0x0FFFFFFF;
+ Y &= 0x0FFFFFFF;
+
+ /*
+ * calculate subkeys
+ */
+ for( i = 0; i < 16; i++ )
+ {
+ if( i < 2 || i == 8 || i == 15 )
+ {
+ X = ((X << 1) | (X >> 27)) & 0x0FFFFFFF;
+ Y = ((Y << 1) | (Y >> 27)) & 0x0FFFFFFF;
+ }
+ else
+ {
+ X = ((X << 2) | (X >> 26)) & 0x0FFFFFFF;
+ Y = ((Y << 2) | (Y >> 26)) & 0x0FFFFFFF;
+ }
+
+ *SK++ = ((X << 4) & 0x24000000) | ((X << 28) & 0x10000000)
+ | ((X << 14) & 0x08000000) | ((X << 18) & 0x02080000)
+ | ((X << 6) & 0x01000000) | ((X << 9) & 0x00200000)
+ | ((X >> 1) & 0x00100000) | ((X << 10) & 0x00040000)
+ | ((X << 2) & 0x00020000) | ((X >> 10) & 0x00010000)
+ | ((Y >> 13) & 0x00002000) | ((Y >> 4) & 0x00001000)
+ | ((Y << 6) & 0x00000800) | ((Y >> 1) & 0x00000400)
+ | ((Y >> 14) & 0x00000200) | ((Y ) & 0x00000100)
+ | ((Y >> 5) & 0x00000020) | ((Y >> 10) & 0x00000010)
+ | ((Y >> 3) & 0x00000008) | ((Y >> 18) & 0x00000004)
+ | ((Y >> 26) & 0x00000002) | ((Y >> 24) & 0x00000001);
+
+ *SK++ = ((X << 15) & 0x20000000) | ((X << 17) & 0x10000000)
+ | ((X << 10) & 0x08000000) | ((X << 22) & 0x04000000)
+ | ((X >> 2) & 0x02000000) | ((X << 1) & 0x01000000)
+ | ((X << 16) & 0x00200000) | ((X << 11) & 0x00100000)
+ | ((X << 3) & 0x00080000) | ((X >> 6) & 0x00040000)
+ | ((X << 15) & 0x00020000) | ((X >> 4) & 0x00010000)
+ | ((Y >> 2) & 0x00002000) | ((Y << 8) & 0x00001000)
+ | ((Y >> 14) & 0x00000808) | ((Y >> 9) & 0x00000400)
+ | ((Y ) & 0x00000200) | ((Y << 7) & 0x00000100)
+ | ((Y >> 7) & 0x00000020) | ((Y >> 3) & 0x00000011)
+ | ((Y << 2) & 0x00000004) | ((Y >> 21) & 0x00000002);
+ }
+}
+
+/*
+ * DES key schedule (56-bit, encryption)
+ */
+void des_setkey_enc( des_context *ctx, unsigned char key[8] )
+{
+ des_setkey( ctx->sk, key );
+}
+
+/*
+ * DES key schedule (56-bit, decryption)
+ */
+void des_setkey_dec( des_context *ctx, unsigned char key[8] )
+{
+ int i;
+
+ des_setkey( ctx->sk, key );
+
+ for( i = 0; i < 16; i += 2 )
+ {
+ SWAP( ctx->sk[i ], ctx->sk[30 - i] );
+ SWAP( ctx->sk[i + 1], ctx->sk[31 - i] );
+ }
+}
+
+/*
+ * DES-ECB block encryption/decryption
+ */
+void des_crypt_ecb( des_context *ctx,
+ const unsigned char input[8],
+ unsigned char output[8] )
+{
+ int i;
+ unsigned long X, Y, T, *SK;
+
+ SK = ctx->sk;
+
+ GET_ULONG_BE( X, input, 0 );
+ GET_ULONG_BE( Y, input, 4 );
+
+ DES_IP( X, Y );
+
+ for( i = 0; i < 8; i++ )
+ {
+ DES_ROUND( Y, X );
+ DES_ROUND( X, Y );
+ }
+
+ DES_FP( Y, X );
+
+ PUT_ULONG_BE( Y, output, 0 );
+ PUT_ULONG_BE( X, output, 4 );
+}
+
+#endif /* PPP_SUPPORT && LWIP_INCLUDED_POLARSSL_DES */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/polarssl/md4.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/polarssl/md4.c
new file mode 100644
index 0000000..b1701a0
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/polarssl/md4.c
@@ -0,0 +1,281 @@
+/*
+ * RFC 1186/1320 compliant MD4 implementation
+ *
+ * Based on XySSL: Copyright (C) 2006-2008 Christophe Devine
+ *
+ * Copyright (C) 2009 Paul Bakker <polarssl_maintainer at polarssl dot org>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the names of PolarSSL or XySSL 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 COPYRIGHT HOLDERS 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 COPYRIGHT
+ * OWNER 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.
+ */
+/*
+ * The MD4 algorithm was designed by Ron Rivest in 1990.
+ *
+ * http://www.ietf.org/rfc/rfc1186.txt
+ * http://www.ietf.org/rfc/rfc1320.txt
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT && LWIP_INCLUDED_POLARSSL_MD4
+
+#include "netif/ppp/polarssl/md4.h"
+
+#include <string.h>
+
+/*
+ * 32-bit integer manipulation macros (little endian)
+ */
+#ifndef GET_ULONG_LE
+#define GET_ULONG_LE(n,b,i) \
+{ \
+ (n) = ( (unsigned long) (b)[(i) ] ) \
+ | ( (unsigned long) (b)[(i) + 1] << 8 ) \
+ | ( (unsigned long) (b)[(i) + 2] << 16 ) \
+ | ( (unsigned long) (b)[(i) + 3] << 24 ); \
+}
+#endif
+
+#ifndef PUT_ULONG_LE
+#define PUT_ULONG_LE(n,b,i) \
+{ \
+ (b)[(i) ] = (unsigned char) ( (n) ); \
+ (b)[(i) + 1] = (unsigned char) ( (n) >> 8 ); \
+ (b)[(i) + 2] = (unsigned char) ( (n) >> 16 ); \
+ (b)[(i) + 3] = (unsigned char) ( (n) >> 24 ); \
+}
+#endif
+
+/*
+ * MD4 context setup
+ */
+void md4_starts( md4_context *ctx )
+{
+ ctx->total[0] = 0;
+ ctx->total[1] = 0;
+
+ ctx->state[0] = 0x67452301;
+ ctx->state[1] = 0xEFCDAB89;
+ ctx->state[2] = 0x98BADCFE;
+ ctx->state[3] = 0x10325476;
+}
+
+static void md4_process( md4_context *ctx, const unsigned char data[64] )
+{
+ unsigned long X[16], A, B, C, D;
+
+ GET_ULONG_LE( X[ 0], data, 0 );
+ GET_ULONG_LE( X[ 1], data, 4 );
+ GET_ULONG_LE( X[ 2], data, 8 );
+ GET_ULONG_LE( X[ 3], data, 12 );
+ GET_ULONG_LE( X[ 4], data, 16 );
+ GET_ULONG_LE( X[ 5], data, 20 );
+ GET_ULONG_LE( X[ 6], data, 24 );
+ GET_ULONG_LE( X[ 7], data, 28 );
+ GET_ULONG_LE( X[ 8], data, 32 );
+ GET_ULONG_LE( X[ 9], data, 36 );
+ GET_ULONG_LE( X[10], data, 40 );
+ GET_ULONG_LE( X[11], data, 44 );
+ GET_ULONG_LE( X[12], data, 48 );
+ GET_ULONG_LE( X[13], data, 52 );
+ GET_ULONG_LE( X[14], data, 56 );
+ GET_ULONG_LE( X[15], data, 60 );
+
+#define S(x,n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n)))
+
+ A = ctx->state[0];
+ B = ctx->state[1];
+ C = ctx->state[2];
+ D = ctx->state[3];
+
+#define F(x, y, z) ((x & y) | ((~x) & z))
+#define P(a,b,c,d,x,s) { a += F(b,c,d) + x; a = S(a,s); }
+
+ P( A, B, C, D, X[ 0], 3 );
+ P( D, A, B, C, X[ 1], 7 );
+ P( C, D, A, B, X[ 2], 11 );
+ P( B, C, D, A, X[ 3], 19 );
+ P( A, B, C, D, X[ 4], 3 );
+ P( D, A, B, C, X[ 5], 7 );
+ P( C, D, A, B, X[ 6], 11 );
+ P( B, C, D, A, X[ 7], 19 );
+ P( A, B, C, D, X[ 8], 3 );
+ P( D, A, B, C, X[ 9], 7 );
+ P( C, D, A, B, X[10], 11 );
+ P( B, C, D, A, X[11], 19 );
+ P( A, B, C, D, X[12], 3 );
+ P( D, A, B, C, X[13], 7 );
+ P( C, D, A, B, X[14], 11 );
+ P( B, C, D, A, X[15], 19 );
+
+#undef P
+#undef F
+
+#define F(x,y,z) ((x & y) | (x & z) | (y & z))
+#define P(a,b,c,d,x,s) { a += F(b,c,d) + x + 0x5A827999; a = S(a,s); }
+
+ P( A, B, C, D, X[ 0], 3 );
+ P( D, A, B, C, X[ 4], 5 );
+ P( C, D, A, B, X[ 8], 9 );
+ P( B, C, D, A, X[12], 13 );
+ P( A, B, C, D, X[ 1], 3 );
+ P( D, A, B, C, X[ 5], 5 );
+ P( C, D, A, B, X[ 9], 9 );
+ P( B, C, D, A, X[13], 13 );
+ P( A, B, C, D, X[ 2], 3 );
+ P( D, A, B, C, X[ 6], 5 );
+ P( C, D, A, B, X[10], 9 );
+ P( B, C, D, A, X[14], 13 );
+ P( A, B, C, D, X[ 3], 3 );
+ P( D, A, B, C, X[ 7], 5 );
+ P( C, D, A, B, X[11], 9 );
+ P( B, C, D, A, X[15], 13 );
+
+#undef P
+#undef F
+
+#define F(x,y,z) (x ^ y ^ z)
+#define P(a,b,c,d,x,s) { a += F(b,c,d) + x + 0x6ED9EBA1; a = S(a,s); }
+
+ P( A, B, C, D, X[ 0], 3 );
+ P( D, A, B, C, X[ 8], 9 );
+ P( C, D, A, B, X[ 4], 11 );
+ P( B, C, D, A, X[12], 15 );
+ P( A, B, C, D, X[ 2], 3 );
+ P( D, A, B, C, X[10], 9 );
+ P( C, D, A, B, X[ 6], 11 );
+ P( B, C, D, A, X[14], 15 );
+ P( A, B, C, D, X[ 1], 3 );
+ P( D, A, B, C, X[ 9], 9 );
+ P( C, D, A, B, X[ 5], 11 );
+ P( B, C, D, A, X[13], 15 );
+ P( A, B, C, D, X[ 3], 3 );
+ P( D, A, B, C, X[11], 9 );
+ P( C, D, A, B, X[ 7], 11 );
+ P( B, C, D, A, X[15], 15 );
+
+#undef F
+#undef P
+
+ ctx->state[0] += A;
+ ctx->state[1] += B;
+ ctx->state[2] += C;
+ ctx->state[3] += D;
+}
+
+/*
+ * MD4 process buffer
+ */
+void md4_update( md4_context *ctx, const unsigned char *input, int ilen )
+{
+ int fill;
+ unsigned long left;
+
+ if( ilen <= 0 )
+ return;
+
+ left = ctx->total[0] & 0x3F;
+ fill = 64 - left;
+
+ ctx->total[0] += ilen;
+ ctx->total[0] &= 0xFFFFFFFF;
+
+ if( ctx->total[0] < (unsigned long) ilen )
+ ctx->total[1]++;
+
+ if( left && ilen >= fill )
+ {
+ MEMCPY( (void *) (ctx->buffer + left),
+ input, fill );
+ md4_process( ctx, ctx->buffer );
+ input += fill;
+ ilen -= fill;
+ left = 0;
+ }
+
+ while( ilen >= 64 )
+ {
+ md4_process( ctx, input );
+ input += 64;
+ ilen -= 64;
+ }
+
+ if( ilen > 0 )
+ {
+ MEMCPY( (void *) (ctx->buffer + left),
+ input, ilen );
+ }
+}
+
+static const unsigned char md4_padding[64] =
+{
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+/*
+ * MD4 final digest
+ */
+void md4_finish( md4_context *ctx, unsigned char output[16] )
+{
+ unsigned long last, padn;
+ unsigned long high, low;
+ unsigned char msglen[8];
+
+ high = ( ctx->total[0] >> 29 )
+ | ( ctx->total[1] << 3 );
+ low = ( ctx->total[0] << 3 );
+
+ PUT_ULONG_LE( low, msglen, 0 );
+ PUT_ULONG_LE( high, msglen, 4 );
+
+ last = ctx->total[0] & 0x3F;
+ padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last );
+
+ md4_update( ctx, md4_padding, padn );
+ md4_update( ctx, msglen, 8 );
+
+ PUT_ULONG_LE( ctx->state[0], output, 0 );
+ PUT_ULONG_LE( ctx->state[1], output, 4 );
+ PUT_ULONG_LE( ctx->state[2], output, 8 );
+ PUT_ULONG_LE( ctx->state[3], output, 12 );
+}
+
+/*
+ * output = MD4( input buffer )
+ */
+void md4( unsigned char *input, int ilen, unsigned char output[16] )
+{
+ md4_context ctx;
+
+ md4_starts( &ctx );
+ md4_update( &ctx, input, ilen );
+ md4_finish( &ctx, output );
+}
+
+#endif /* PPP_SUPPORT && LWIP_INCLUDED_POLARSSL_MD4 */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/polarssl/md5.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/polarssl/md5.c
new file mode 100644
index 0000000..1ec4d81
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/polarssl/md5.c
@@ -0,0 +1,300 @@
+/*
+ * RFC 1321 compliant MD5 implementation
+ *
+ * Based on XySSL: Copyright (C) 2006-2008 Christophe Devine
+ *
+ * Copyright (C) 2009 Paul Bakker <polarssl_maintainer at polarssl dot org>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the names of PolarSSL or XySSL 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 COPYRIGHT HOLDERS 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 COPYRIGHT
+ * OWNER 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.
+ */
+/*
+ * The MD5 algorithm was designed by Ron Rivest in 1991.
+ *
+ * http://www.ietf.org/rfc/rfc1321.txt
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT && LWIP_INCLUDED_POLARSSL_MD5
+
+#include "netif/ppp/polarssl/md5.h"
+
+#include <string.h>
+
+/*
+ * 32-bit integer manipulation macros (little endian)
+ */
+#ifndef GET_ULONG_LE
+#define GET_ULONG_LE(n,b,i) \
+{ \
+ (n) = ( (unsigned long) (b)[(i) ] ) \
+ | ( (unsigned long) (b)[(i) + 1] << 8 ) \
+ | ( (unsigned long) (b)[(i) + 2] << 16 ) \
+ | ( (unsigned long) (b)[(i) + 3] << 24 ); \
+}
+#endif
+
+#ifndef PUT_ULONG_LE
+#define PUT_ULONG_LE(n,b,i) \
+{ \
+ (b)[(i) ] = (unsigned char) ( (n) ); \
+ (b)[(i) + 1] = (unsigned char) ( (n) >> 8 ); \
+ (b)[(i) + 2] = (unsigned char) ( (n) >> 16 ); \
+ (b)[(i) + 3] = (unsigned char) ( (n) >> 24 ); \
+}
+#endif
+
+/*
+ * MD5 context setup
+ */
+void md5_starts( md5_context *ctx )
+{
+ ctx->total[0] = 0;
+ ctx->total[1] = 0;
+
+ ctx->state[0] = 0x67452301;
+ ctx->state[1] = 0xEFCDAB89;
+ ctx->state[2] = 0x98BADCFE;
+ ctx->state[3] = 0x10325476;
+}
+
+static void md5_process( md5_context *ctx, const unsigned char data[64] )
+{
+ unsigned long X[16], A, B, C, D;
+
+ GET_ULONG_LE( X[ 0], data, 0 );
+ GET_ULONG_LE( X[ 1], data, 4 );
+ GET_ULONG_LE( X[ 2], data, 8 );
+ GET_ULONG_LE( X[ 3], data, 12 );
+ GET_ULONG_LE( X[ 4], data, 16 );
+ GET_ULONG_LE( X[ 5], data, 20 );
+ GET_ULONG_LE( X[ 6], data, 24 );
+ GET_ULONG_LE( X[ 7], data, 28 );
+ GET_ULONG_LE( X[ 8], data, 32 );
+ GET_ULONG_LE( X[ 9], data, 36 );
+ GET_ULONG_LE( X[10], data, 40 );
+ GET_ULONG_LE( X[11], data, 44 );
+ GET_ULONG_LE( X[12], data, 48 );
+ GET_ULONG_LE( X[13], data, 52 );
+ GET_ULONG_LE( X[14], data, 56 );
+ GET_ULONG_LE( X[15], data, 60 );
+
+#define S(x,n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n)))
+
+#define P(a,b,c,d,k,s,t) \
+{ \
+ a += F(b,c,d) + X[k] + t; a = S(a,s) + b; \
+}
+
+ A = ctx->state[0];
+ B = ctx->state[1];
+ C = ctx->state[2];
+ D = ctx->state[3];
+
+#define F(x,y,z) (z ^ (x & (y ^ z)))
+
+ P( A, B, C, D, 0, 7, 0xD76AA478 );
+ P( D, A, B, C, 1, 12, 0xE8C7B756 );
+ P( C, D, A, B, 2, 17, 0x242070DB );
+ P( B, C, D, A, 3, 22, 0xC1BDCEEE );
+ P( A, B, C, D, 4, 7, 0xF57C0FAF );
+ P( D, A, B, C, 5, 12, 0x4787C62A );
+ P( C, D, A, B, 6, 17, 0xA8304613 );
+ P( B, C, D, A, 7, 22, 0xFD469501 );
+ P( A, B, C, D, 8, 7, 0x698098D8 );
+ P( D, A, B, C, 9, 12, 0x8B44F7AF );
+ P( C, D, A, B, 10, 17, 0xFFFF5BB1 );
+ P( B, C, D, A, 11, 22, 0x895CD7BE );
+ P( A, B, C, D, 12, 7, 0x6B901122 );
+ P( D, A, B, C, 13, 12, 0xFD987193 );
+ P( C, D, A, B, 14, 17, 0xA679438E );
+ P( B, C, D, A, 15, 22, 0x49B40821 );
+
+#undef F
+
+#define F(x,y,z) (y ^ (z & (x ^ y)))
+
+ P( A, B, C, D, 1, 5, 0xF61E2562 );
+ P( D, A, B, C, 6, 9, 0xC040B340 );
+ P( C, D, A, B, 11, 14, 0x265E5A51 );
+ P( B, C, D, A, 0, 20, 0xE9B6C7AA );
+ P( A, B, C, D, 5, 5, 0xD62F105D );
+ P( D, A, B, C, 10, 9, 0x02441453 );
+ P( C, D, A, B, 15, 14, 0xD8A1E681 );
+ P( B, C, D, A, 4, 20, 0xE7D3FBC8 );
+ P( A, B, C, D, 9, 5, 0x21E1CDE6 );
+ P( D, A, B, C, 14, 9, 0xC33707D6 );
+ P( C, D, A, B, 3, 14, 0xF4D50D87 );
+ P( B, C, D, A, 8, 20, 0x455A14ED );
+ P( A, B, C, D, 13, 5, 0xA9E3E905 );
+ P( D, A, B, C, 2, 9, 0xFCEFA3F8 );
+ P( C, D, A, B, 7, 14, 0x676F02D9 );
+ P( B, C, D, A, 12, 20, 0x8D2A4C8A );
+
+#undef F
+
+#define F(x,y,z) (x ^ y ^ z)
+
+ P( A, B, C, D, 5, 4, 0xFFFA3942 );
+ P( D, A, B, C, 8, 11, 0x8771F681 );
+ P( C, D, A, B, 11, 16, 0x6D9D6122 );
+ P( B, C, D, A, 14, 23, 0xFDE5380C );
+ P( A, B, C, D, 1, 4, 0xA4BEEA44 );
+ P( D, A, B, C, 4, 11, 0x4BDECFA9 );
+ P( C, D, A, B, 7, 16, 0xF6BB4B60 );
+ P( B, C, D, A, 10, 23, 0xBEBFBC70 );
+ P( A, B, C, D, 13, 4, 0x289B7EC6 );
+ P( D, A, B, C, 0, 11, 0xEAA127FA );
+ P( C, D, A, B, 3, 16, 0xD4EF3085 );
+ P( B, C, D, A, 6, 23, 0x04881D05 );
+ P( A, B, C, D, 9, 4, 0xD9D4D039 );
+ P( D, A, B, C, 12, 11, 0xE6DB99E5 );
+ P( C, D, A, B, 15, 16, 0x1FA27CF8 );
+ P( B, C, D, A, 2, 23, 0xC4AC5665 );
+
+#undef F
+
+#define F(x,y,z) (y ^ (x | ~z))
+
+ P( A, B, C, D, 0, 6, 0xF4292244 );
+ P( D, A, B, C, 7, 10, 0x432AFF97 );
+ P( C, D, A, B, 14, 15, 0xAB9423A7 );
+ P( B, C, D, A, 5, 21, 0xFC93A039 );
+ P( A, B, C, D, 12, 6, 0x655B59C3 );
+ P( D, A, B, C, 3, 10, 0x8F0CCC92 );
+ P( C, D, A, B, 10, 15, 0xFFEFF47D );
+ P( B, C, D, A, 1, 21, 0x85845DD1 );
+ P( A, B, C, D, 8, 6, 0x6FA87E4F );
+ P( D, A, B, C, 15, 10, 0xFE2CE6E0 );
+ P( C, D, A, B, 6, 15, 0xA3014314 );
+ P( B, C, D, A, 13, 21, 0x4E0811A1 );
+ P( A, B, C, D, 4, 6, 0xF7537E82 );
+ P( D, A, B, C, 11, 10, 0xBD3AF235 );
+ P( C, D, A, B, 2, 15, 0x2AD7D2BB );
+ P( B, C, D, A, 9, 21, 0xEB86D391 );
+
+#undef F
+
+ ctx->state[0] += A;
+ ctx->state[1] += B;
+ ctx->state[2] += C;
+ ctx->state[3] += D;
+}
+
+/*
+ * MD5 process buffer
+ */
+void md5_update( md5_context *ctx, const unsigned char *input, int ilen )
+{
+ int fill;
+ unsigned long left;
+
+ if( ilen <= 0 )
+ return;
+
+ left = ctx->total[0] & 0x3F;
+ fill = 64 - left;
+
+ ctx->total[0] += ilen;
+ ctx->total[0] &= 0xFFFFFFFF;
+
+ if( ctx->total[0] < (unsigned long) ilen )
+ ctx->total[1]++;
+
+ if( left && ilen >= fill )
+ {
+ MEMCPY( (void *) (ctx->buffer + left),
+ input, fill );
+ md5_process( ctx, ctx->buffer );
+ input += fill;
+ ilen -= fill;
+ left = 0;
+ }
+
+ while( ilen >= 64 )
+ {
+ md5_process( ctx, input );
+ input += 64;
+ ilen -= 64;
+ }
+
+ if( ilen > 0 )
+ {
+ MEMCPY( (void *) (ctx->buffer + left),
+ input, ilen );
+ }
+}
+
+static const unsigned char md5_padding[64] =
+{
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+/*
+ * MD5 final digest
+ */
+void md5_finish( md5_context *ctx, unsigned char output[16] )
+{
+ unsigned long last, padn;
+ unsigned long high, low;
+ unsigned char msglen[8];
+
+ high = ( ctx->total[0] >> 29 )
+ | ( ctx->total[1] << 3 );
+ low = ( ctx->total[0] << 3 );
+
+ PUT_ULONG_LE( low, msglen, 0 );
+ PUT_ULONG_LE( high, msglen, 4 );
+
+ last = ctx->total[0] & 0x3F;
+ padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last );
+
+ md5_update( ctx, md5_padding, padn );
+ md5_update( ctx, msglen, 8 );
+
+ PUT_ULONG_LE( ctx->state[0], output, 0 );
+ PUT_ULONG_LE( ctx->state[1], output, 4 );
+ PUT_ULONG_LE( ctx->state[2], output, 8 );
+ PUT_ULONG_LE( ctx->state[3], output, 12 );
+}
+
+/*
+ * output = MD5( input buffer )
+ */
+void md5( unsigned char *input, int ilen, unsigned char output[16] )
+{
+ md5_context ctx;
+
+ md5_starts( &ctx );
+ md5_update( &ctx, input, ilen );
+ md5_finish( &ctx, output );
+}
+
+#endif /* PPP_SUPPORT && LWIP_INCLUDED_POLARSSL_MD5 */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/polarssl/sha1.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/polarssl/sha1.c
new file mode 100644
index 0000000..c2192ea
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/polarssl/sha1.c
@@ -0,0 +1,335 @@
+/*
+ * FIPS-180-1 compliant SHA-1 implementation
+ *
+ * Based on XySSL: Copyright (C) 2006-2008 Christophe Devine
+ *
+ * Copyright (C) 2009 Paul Bakker <polarssl_maintainer at polarssl dot org>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the names of PolarSSL or XySSL 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 COPYRIGHT HOLDERS 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 COPYRIGHT
+ * OWNER 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.
+ */
+/*
+ * The SHA-1 standard was published by NIST in 1993.
+ *
+ * http://www.itl.nist.gov/fipspubs/fip180-1.htm
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT && LWIP_INCLUDED_POLARSSL_SHA1
+
+#include "netif/ppp/polarssl/sha1.h"
+
+#include <string.h>
+
+/*
+ * 32-bit integer manipulation macros (big endian)
+ */
+#ifndef GET_ULONG_BE
+#define GET_ULONG_BE(n,b,i) \
+{ \
+ (n) = ( (unsigned long) (b)[(i) ] << 24 ) \
+ | ( (unsigned long) (b)[(i) + 1] << 16 ) \
+ | ( (unsigned long) (b)[(i) + 2] << 8 ) \
+ | ( (unsigned long) (b)[(i) + 3] ); \
+}
+#endif
+
+#ifndef PUT_ULONG_BE
+#define PUT_ULONG_BE(n,b,i) \
+{ \
+ (b)[(i) ] = (unsigned char) ( (n) >> 24 ); \
+ (b)[(i) + 1] = (unsigned char) ( (n) >> 16 ); \
+ (b)[(i) + 2] = (unsigned char) ( (n) >> 8 ); \
+ (b)[(i) + 3] = (unsigned char) ( (n) ); \
+}
+#endif
+
+/*
+ * SHA-1 context setup
+ */
+void sha1_starts( sha1_context *ctx )
+{
+ ctx->total[0] = 0;
+ ctx->total[1] = 0;
+
+ ctx->state[0] = 0x67452301;
+ ctx->state[1] = 0xEFCDAB89;
+ ctx->state[2] = 0x98BADCFE;
+ ctx->state[3] = 0x10325476;
+ ctx->state[4] = 0xC3D2E1F0;
+}
+
+static void sha1_process( sha1_context *ctx, const unsigned char data[64] )
+{
+ unsigned long temp, W[16], A, B, C, D, E;
+
+ GET_ULONG_BE( W[ 0], data, 0 );
+ GET_ULONG_BE( W[ 1], data, 4 );
+ GET_ULONG_BE( W[ 2], data, 8 );
+ GET_ULONG_BE( W[ 3], data, 12 );
+ GET_ULONG_BE( W[ 4], data, 16 );
+ GET_ULONG_BE( W[ 5], data, 20 );
+ GET_ULONG_BE( W[ 6], data, 24 );
+ GET_ULONG_BE( W[ 7], data, 28 );
+ GET_ULONG_BE( W[ 8], data, 32 );
+ GET_ULONG_BE( W[ 9], data, 36 );
+ GET_ULONG_BE( W[10], data, 40 );
+ GET_ULONG_BE( W[11], data, 44 );
+ GET_ULONG_BE( W[12], data, 48 );
+ GET_ULONG_BE( W[13], data, 52 );
+ GET_ULONG_BE( W[14], data, 56 );
+ GET_ULONG_BE( W[15], data, 60 );
+
+#define S(x,n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n)))
+
+#define R(t) \
+( \
+ temp = W[(t - 3) & 0x0F] ^ W[(t - 8) & 0x0F] ^ \
+ W[(t - 14) & 0x0F] ^ W[ t & 0x0F], \
+ ( W[t & 0x0F] = S(temp,1) ) \
+)
+
+#define P(a,b,c,d,e,x) \
+{ \
+ e += S(a,5) + F(b,c,d) + K + x; b = S(b,30); \
+}
+
+ A = ctx->state[0];
+ B = ctx->state[1];
+ C = ctx->state[2];
+ D = ctx->state[3];
+ E = ctx->state[4];
+
+#define F(x,y,z) (z ^ (x & (y ^ z)))
+#define K 0x5A827999
+
+ P( A, B, C, D, E, W[0] );
+ P( E, A, B, C, D, W[1] );
+ P( D, E, A, B, C, W[2] );
+ P( C, D, E, A, B, W[3] );
+ P( B, C, D, E, A, W[4] );
+ P( A, B, C, D, E, W[5] );
+ P( E, A, B, C, D, W[6] );
+ P( D, E, A, B, C, W[7] );
+ P( C, D, E, A, B, W[8] );
+ P( B, C, D, E, A, W[9] );
+ P( A, B, C, D, E, W[10] );
+ P( E, A, B, C, D, W[11] );
+ P( D, E, A, B, C, W[12] );
+ P( C, D, E, A, B, W[13] );
+ P( B, C, D, E, A, W[14] );
+ P( A, B, C, D, E, W[15] );
+ P( E, A, B, C, D, R(16) );
+ P( D, E, A, B, C, R(17) );
+ P( C, D, E, A, B, R(18) );
+ P( B, C, D, E, A, R(19) );
+
+#undef K
+#undef F
+
+#define F(x,y,z) (x ^ y ^ z)
+#define K 0x6ED9EBA1
+
+ P( A, B, C, D, E, R(20) );
+ P( E, A, B, C, D, R(21) );
+ P( D, E, A, B, C, R(22) );
+ P( C, D, E, A, B, R(23) );
+ P( B, C, D, E, A, R(24) );
+ P( A, B, C, D, E, R(25) );
+ P( E, A, B, C, D, R(26) );
+ P( D, E, A, B, C, R(27) );
+ P( C, D, E, A, B, R(28) );
+ P( B, C, D, E, A, R(29) );
+ P( A, B, C, D, E, R(30) );
+ P( E, A, B, C, D, R(31) );
+ P( D, E, A, B, C, R(32) );
+ P( C, D, E, A, B, R(33) );
+ P( B, C, D, E, A, R(34) );
+ P( A, B, C, D, E, R(35) );
+ P( E, A, B, C, D, R(36) );
+ P( D, E, A, B, C, R(37) );
+ P( C, D, E, A, B, R(38) );
+ P( B, C, D, E, A, R(39) );
+
+#undef K
+#undef F
+
+#define F(x,y,z) ((x & y) | (z & (x | y)))
+#define K 0x8F1BBCDC
+
+ P( A, B, C, D, E, R(40) );
+ P( E, A, B, C, D, R(41) );
+ P( D, E, A, B, C, R(42) );
+ P( C, D, E, A, B, R(43) );
+ P( B, C, D, E, A, R(44) );
+ P( A, B, C, D, E, R(45) );
+ P( E, A, B, C, D, R(46) );
+ P( D, E, A, B, C, R(47) );
+ P( C, D, E, A, B, R(48) );
+ P( B, C, D, E, A, R(49) );
+ P( A, B, C, D, E, R(50) );
+ P( E, A, B, C, D, R(51) );
+ P( D, E, A, B, C, R(52) );
+ P( C, D, E, A, B, R(53) );
+ P( B, C, D, E, A, R(54) );
+ P( A, B, C, D, E, R(55) );
+ P( E, A, B, C, D, R(56) );
+ P( D, E, A, B, C, R(57) );
+ P( C, D, E, A, B, R(58) );
+ P( B, C, D, E, A, R(59) );
+
+#undef K
+#undef F
+
+#define F(x,y,z) (x ^ y ^ z)
+#define K 0xCA62C1D6
+
+ P( A, B, C, D, E, R(60) );
+ P( E, A, B, C, D, R(61) );
+ P( D, E, A, B, C, R(62) );
+ P( C, D, E, A, B, R(63) );
+ P( B, C, D, E, A, R(64) );
+ P( A, B, C, D, E, R(65) );
+ P( E, A, B, C, D, R(66) );
+ P( D, E, A, B, C, R(67) );
+ P( C, D, E, A, B, R(68) );
+ P( B, C, D, E, A, R(69) );
+ P( A, B, C, D, E, R(70) );
+ P( E, A, B, C, D, R(71) );
+ P( D, E, A, B, C, R(72) );
+ P( C, D, E, A, B, R(73) );
+ P( B, C, D, E, A, R(74) );
+ P( A, B, C, D, E, R(75) );
+ P( E, A, B, C, D, R(76) );
+ P( D, E, A, B, C, R(77) );
+ P( C, D, E, A, B, R(78) );
+ P( B, C, D, E, A, R(79) );
+
+#undef K
+#undef F
+
+ ctx->state[0] += A;
+ ctx->state[1] += B;
+ ctx->state[2] += C;
+ ctx->state[3] += D;
+ ctx->state[4] += E;
+}
+
+/*
+ * SHA-1 process buffer
+ */
+void sha1_update( sha1_context *ctx, const unsigned char *input, int ilen )
+{
+ int fill;
+ unsigned long left;
+
+ if( ilen <= 0 )
+ return;
+
+ left = ctx->total[0] & 0x3F;
+ fill = 64 - left;
+
+ ctx->total[0] += ilen;
+ ctx->total[0] &= 0xFFFFFFFF;
+
+ if( ctx->total[0] < (unsigned long) ilen )
+ ctx->total[1]++;
+
+ if( left && ilen >= fill )
+ {
+ MEMCPY( (void *) (ctx->buffer + left),
+ input, fill );
+ sha1_process( ctx, ctx->buffer );
+ input += fill;
+ ilen -= fill;
+ left = 0;
+ }
+
+ while( ilen >= 64 )
+ {
+ sha1_process( ctx, input );
+ input += 64;
+ ilen -= 64;
+ }
+
+ if( ilen > 0 )
+ {
+ MEMCPY( (void *) (ctx->buffer + left),
+ input, ilen );
+ }
+}
+
+static const unsigned char sha1_padding[64] =
+{
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+/*
+ * SHA-1 final digest
+ */
+void sha1_finish( sha1_context *ctx, unsigned char output[20] )
+{
+ unsigned long last, padn;
+ unsigned long high, low;
+ unsigned char msglen[8];
+
+ high = ( ctx->total[0] >> 29 )
+ | ( ctx->total[1] << 3 );
+ low = ( ctx->total[0] << 3 );
+
+ PUT_ULONG_BE( high, msglen, 0 );
+ PUT_ULONG_BE( low, msglen, 4 );
+
+ last = ctx->total[0] & 0x3F;
+ padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last );
+
+ sha1_update( ctx, sha1_padding, padn );
+ sha1_update( ctx, msglen, 8 );
+
+ PUT_ULONG_BE( ctx->state[0], output, 0 );
+ PUT_ULONG_BE( ctx->state[1], output, 4 );
+ PUT_ULONG_BE( ctx->state[2], output, 8 );
+ PUT_ULONG_BE( ctx->state[3], output, 12 );
+ PUT_ULONG_BE( ctx->state[4], output, 16 );
+}
+
+/*
+ * output = SHA-1( input buffer )
+ */
+void sha1( unsigned char *input, int ilen, unsigned char output[20] )
+{
+ sha1_context ctx;
+
+ sha1_starts( &ctx );
+ sha1_update( &ctx, input, ilen );
+ sha1_finish( &ctx, output );
+}
+
+#endif /* PPP_SUPPORT && LWIP_INCLUDED_POLARSSL_SHA1 */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/ppp.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/ppp.c
new file mode 100644
index 0000000..8b77765
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/ppp.c
@@ -0,0 +1,1647 @@
+/*****************************************************************************
+* 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.
+ */
+
+/**
+ * @defgroup ppp PPP netif
+ * @ingroup addons
+ * @verbinclude "ppp.txt"
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/pbuf.h"
+#include "lwip/stats.h"
+#include "lwip/sys.h"
+#include "lwip/tcpip.h"
+#include "lwip/api.h"
+#include "lwip/snmp.h"
+#include "lwip/ip4.h" /* for ip4_input() */
+#if PPP_IPV6_SUPPORT
+#include "lwip/ip6.h" /* for ip6_input() */
+#endif /* PPP_IPV6_SUPPORT */
+#include "lwip/dns.h"
+
+#include "netif/ppp/ppp_impl.h"
+#include "netif/ppp/pppos.h"
+
+#include "netif/ppp/fsm.h"
+#include "netif/ppp/lcp.h"
+#include "netif/ppp/magic.h"
+
+#if PAP_SUPPORT
+#include "netif/ppp/upap.h"
+#endif /* PAP_SUPPORT */
+#if CHAP_SUPPORT
+#include "netif/ppp/chap-new.h"
+#endif /* CHAP_SUPPORT */
+#if EAP_SUPPORT
+#include "netif/ppp/eap.h"
+#endif /* EAP_SUPPORT */
+#if CCP_SUPPORT
+#include "netif/ppp/ccp.h"
+#endif /* CCP_SUPPORT */
+#if MPPE_SUPPORT
+#include "netif/ppp/mppe.h"
+#endif /* MPPE_SUPPORT */
+#if ECP_SUPPORT
+#include "netif/ppp/ecp.h"
+#endif /* EAP_SUPPORT */
+#if VJ_SUPPORT
+#include "netif/ppp/vj.h"
+#endif /* VJ_SUPPORT */
+#if PPP_IPV4_SUPPORT
+#include "netif/ppp/ipcp.h"
+#endif /* PPP_IPV4_SUPPORT */
+#if PPP_IPV6_SUPPORT
+#include "netif/ppp/ipv6cp.h"
+#endif /* PPP_IPV6_SUPPORT */
+
+/*************************/
+/*** LOCAL DEFINITIONS ***/
+/*************************/
+
+/* Memory pools */
+#if PPPOS_SUPPORT
+LWIP_MEMPOOL_PROTOTYPE(PPPOS_PCB);
+#endif
+#if PPPOE_SUPPORT
+LWIP_MEMPOOL_PROTOTYPE(PPPOE_IF);
+#endif
+#if PPPOL2TP_SUPPORT
+LWIP_MEMPOOL_PROTOTYPE(PPPOL2TP_PCB);
+#endif
+#if LWIP_PPP_API && LWIP_MPU_COMPATIBLE
+LWIP_MEMPOOL_PROTOTYPE(PPPAPI_MSG);
+#endif
+LWIP_MEMPOOL_DECLARE(PPP_PCB, MEMP_NUM_PPP_PCB, sizeof(ppp_pcb), "PPP_PCB")
+
+/* FIXME: add stats per PPP session */
+#if PPP_STATS_SUPPORT
+static struct timeval start_time; /* Time when link was started. */
+static struct pppd_stats old_link_stats;
+struct pppd_stats link_stats;
+unsigned link_connect_time;
+int link_stats_valid;
+#endif /* PPP_STATS_SUPPORT */
+
+/*
+ * PPP Data Link Layer "protocol" table.
+ * One entry per supported protocol.
+ * The last entry must be NULL.
+ */
+const struct protent* const 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 */
+#if PPP_IPV4_SUPPORT
+ &ipcp_protent,
+#endif /* PPP_IPV4_SUPPORT */
+#if PPP_IPV6_SUPPORT
+ &ipv6cp_protent,
+#endif /* PPP_IPV6_SUPPORT */
+#if CCP_SUPPORT
+ &ccp_protent,
+#endif /* CCP_SUPPORT */
+#if ECP_SUPPORT
+ &ecp_protent,
+#endif /* ECP_SUPPORT */
+#ifdef AT_CHANGE
+ &atcp_protent,
+#endif /* AT_CHANGE */
+#if EAP_SUPPORT
+ &eap_protent,
+#endif /* EAP_SUPPORT */
+ NULL
+};
+
+/* Prototypes for procedures local to this file. */
+static void ppp_do_connect(void *arg);
+static err_t ppp_netif_init_cb(struct netif *netif);
+#if LWIP_IPV4
+static err_t ppp_netif_output_ip4(struct netif *netif, struct pbuf *pb, const ip4_addr_t *ipaddr);
+#endif /* LWIP_IPV4 */
+#if PPP_IPV6_SUPPORT
+static err_t ppp_netif_output_ip6(struct netif *netif, struct pbuf *pb, const ip6_addr_t *ipaddr);
+#endif /* PPP_IPV6_SUPPORT */
+static err_t ppp_netif_output(struct netif *netif, struct pbuf *pb, u16_t protocol);
+
+/***********************************/
+/*** PUBLIC FUNCTION DEFINITIONS ***/
+/***********************************/
+#if PPP_AUTH_SUPPORT
+void ppp_set_auth(ppp_pcb *pcb, u8_t authtype, const char *user, const char *passwd) {
+#if PAP_SUPPORT
+ pcb->settings.refuse_pap = !(authtype & PPPAUTHTYPE_PAP);
+#endif /* PAP_SUPPORT */
+#if CHAP_SUPPORT
+ pcb->settings.refuse_chap = !(authtype & PPPAUTHTYPE_CHAP);
+#if MSCHAP_SUPPORT
+ pcb->settings.refuse_mschap = !(authtype & PPPAUTHTYPE_MSCHAP);
+ pcb->settings.refuse_mschap_v2 = !(authtype & PPPAUTHTYPE_MSCHAP_V2);
+#endif /* MSCHAP_SUPPORT */
+#endif /* CHAP_SUPPORT */
+#if EAP_SUPPORT
+ pcb->settings.refuse_eap = !(authtype & PPPAUTHTYPE_EAP);
+#endif /* EAP_SUPPORT */
+ pcb->settings.user = user;
+ pcb->settings.passwd = passwd;
+}
+#endif /* PPP_AUTH_SUPPORT */
+
+#if MPPE_SUPPORT
+/* Set MPPE configuration */
+void ppp_set_mppe(ppp_pcb *pcb, u8_t flags) {
+ if (flags == PPP_MPPE_DISABLE) {
+ pcb->settings.require_mppe = 0;
+ return;
+ }
+
+ pcb->settings.require_mppe = 1;
+ pcb->settings.refuse_mppe_stateful = !(flags & PPP_MPPE_ALLOW_STATEFUL);
+ pcb->settings.refuse_mppe_40 = !!(flags & PPP_MPPE_REFUSE_40);
+ pcb->settings.refuse_mppe_128 = !!(flags & PPP_MPPE_REFUSE_128);
+}
+#endif /* MPPE_SUPPORT */
+
+#if PPP_NOTIFY_PHASE
+void ppp_set_notify_phase_callback(ppp_pcb *pcb, ppp_notify_phase_cb_fn notify_phase_cb) {
+ pcb->notify_phase_cb = notify_phase_cb;
+ notify_phase_cb(pcb, pcb->phase, pcb->ctx_cb);
+}
+#endif /* PPP_NOTIFY_PHASE */
+
+/*
+ * Initiate a PPP connection.
+ *
+ * This can only be called if PPP is in the dead phase.
+ *
+ * Holdoff is the time to wait (in seconds) before initiating
+ * the connection.
+ *
+ * If this port connects to a modem, the modem connection must be
+ * established before calling this.
+ */
+err_t ppp_connect(ppp_pcb *pcb, u16_t holdoff) {
+ if (pcb->phase != PPP_PHASE_DEAD) {
+ return ERR_ALREADY;
+ }
+
+ PPPDEBUG(LOG_DEBUG, ("ppp_connect[%d]: holdoff=%d\n", pcb->netif->num, holdoff));
+
+ if (holdoff == 0) {
+ ppp_do_connect(pcb);
+ return ERR_OK;
+ }
+
+ new_phase(pcb, PPP_PHASE_HOLDOFF);
+ sys_timeout((u32_t)(holdoff*1000), ppp_do_connect, pcb);
+ return ERR_OK;
+}
+
+#if PPP_SERVER
+/*
+ * Listen for an incoming PPP connection.
+ *
+ * This can only be called if PPP is in the dead phase.
+ *
+ * If this port connects to a modem, the modem connection must be
+ * established before calling this.
+ */
+err_t ppp_listen(ppp_pcb *pcb) {
+ if (pcb->phase != PPP_PHASE_DEAD) {
+ return ERR_ALREADY;
+ }
+
+ PPPDEBUG(LOG_DEBUG, ("ppp_listen[%d]\n", pcb->netif->num));
+
+ if (pcb->link_cb->listen) {
+ new_phase(pcb, PPP_PHASE_INITIALIZE);
+ pcb->link_cb->listen(pcb, pcb->link_ctx_cb);
+ return ERR_OK;
+ }
+ return ERR_IF;
+}
+#endif /* PPP_SERVER */
+
+/*
+ * Initiate the end of a PPP connection.
+ * Any outstanding packets in the queues are dropped.
+ *
+ * Setting nocarrier to 1 close the PPP connection without initiating the
+ * shutdown procedure. Always using nocarrier = 0 is still recommended,
+ * this is going to take a little longer time if your link is down, but
+ * is a safer choice for the PPP state machine.
+ *
+ * Return 0 on success, an error code on failure.
+ */
+err_t
+ppp_close(ppp_pcb *pcb, u8_t nocarrier)
+{
+ pcb->err_code = PPPERR_USER;
+
+ /* holdoff phase, cancel the reconnection */
+ if (pcb->phase == PPP_PHASE_HOLDOFF) {
+ sys_untimeout(ppp_do_connect, pcb);
+ new_phase(pcb, PPP_PHASE_DEAD);
+ }
+
+ /* dead phase, nothing to do, call the status callback to be consistent */
+ if (pcb->phase == PPP_PHASE_DEAD) {
+ pcb->link_status_cb(pcb, pcb->err_code, pcb->ctx_cb);
+ return ERR_OK;
+ }
+
+ /* Already terminating, nothing to do */
+ if (pcb->phase >= PPP_PHASE_TERMINATE) {
+ return ERR_INPROGRESS;
+ }
+
+ /* LCP not open, close link protocol */
+ if (pcb->phase < PPP_PHASE_ESTABLISH) {
+ new_phase(pcb, PPP_PHASE_DISCONNECT);
+ ppp_link_terminated(pcb);
+ return ERR_OK;
+ }
+
+ /*
+ * Only accept carrier lost signal on the stable running phase in order
+ * to prevent changing the PPP phase FSM in transition phases.
+ *
+ * Always using nocarrier = 0 is still recommended, this is going to
+ * take a little longer time, but is a safer choice from FSM point of view.
+ */
+ if (nocarrier && pcb->phase == PPP_PHASE_RUNNING) {
+ PPPDEBUG(LOG_DEBUG, ("ppp_close[%d]: carrier lost -> lcp_lowerdown\n", pcb->netif->num));
+ lcp_lowerdown(pcb);
+ /* forced link termination, this will force link protocol to disconnect. */
+ link_terminated(pcb);
+ return ERR_OK;
+ }
+
+ /* Disconnect */
+ PPPDEBUG(LOG_DEBUG, ("ppp_close[%d]: kill_link -> lcp_close\n", pcb->netif->num));
+ /* LCP soft close request. */
+ lcp_close(pcb, "User request");
+ return ERR_OK;
+}
+
+/*
+ * Release the control block.
+ *
+ * This can only be called if PPP is in the dead phase.
+ *
+ * You must use ppp_close() before if you wish to terminate
+ * an established PPP session.
+ *
+ * Return 0 on success, an error code on failure.
+ */
+err_t ppp_free(ppp_pcb *pcb) {
+ err_t err;
+ if (pcb->phase != PPP_PHASE_DEAD) {
+ return ERR_CONN;
+ }
+
+ PPPDEBUG(LOG_DEBUG, ("ppp_free[%d]\n", pcb->netif->num));
+
+ netif_remove(pcb->netif);
+
+ err = pcb->link_cb->free(pcb, pcb->link_ctx_cb);
+
+ LWIP_MEMPOOL_FREE(PPP_PCB, pcb);
+ return err;
+}
+
+/* Get and set parameters for the given connection.
+ * Return 0 on success, an error code on failure. */
+err_t
+ppp_ioctl(ppp_pcb *pcb, u8_t cmd, void *arg)
+{
+ if (pcb == NULL) {
+ return ERR_VAL;
+ }
+
+ switch(cmd) {
+ case PPPCTLG_UPSTATUS: /* Get the PPP up status. */
+ if (!arg) {
+ goto fail;
+ }
+ *(int *)arg = (int)(0
+#if PPP_IPV4_SUPPORT
+ || pcb->if4_up
+#endif /* PPP_IPV4_SUPPORT */
+#if PPP_IPV6_SUPPORT
+ || pcb->if6_up
+#endif /* PPP_IPV6_SUPPORT */
+ );
+ return ERR_OK;
+
+ case PPPCTLG_ERRCODE: /* Get the PPP error code. */
+ if (!arg) {
+ goto fail;
+ }
+ *(int *)arg = (int)(pcb->err_code);
+ return ERR_OK;
+
+ default:
+ goto fail;
+ }
+
+fail:
+ return ERR_VAL;
+}
+
+
+/**********************************/
+/*** LOCAL FUNCTION DEFINITIONS ***/
+/**********************************/
+
+static void ppp_do_connect(void *arg) {
+ ppp_pcb *pcb = (ppp_pcb*)arg;
+
+ LWIP_ASSERT("pcb->phase == PPP_PHASE_DEAD || pcb->phase == PPP_PHASE_HOLDOFF", pcb->phase == PPP_PHASE_DEAD || pcb->phase == PPP_PHASE_HOLDOFF);
+
+ new_phase(pcb, PPP_PHASE_INITIALIZE);
+ pcb->link_cb->connect(pcb, pcb->link_ctx_cb);
+}
+
+/*
+ * ppp_netif_init_cb - netif init callback
+ */
+static err_t ppp_netif_init_cb(struct netif *netif) {
+ netif->name[0] = 'p';
+ netif->name[1] = 'p';
+#if LWIP_IPV4
+ /* FIXME: change that when netif_null_output_ip4() will materialize */
+ netif->output = ppp_netif_output_ip4;
+#endif /* LWIP_IPV4 */
+#if PPP_IPV6_SUPPORT
+ netif->output_ip6 = ppp_netif_output_ip6;
+#endif /* PPP_IPV6_SUPPORT */
+ netif->flags = NETIF_FLAG_UP;
+#if LWIP_NETIF_HOSTNAME
+ /* @todo: Initialize interface hostname */
+ /* netif_set_hostname(netif, "lwip"); */
+#endif /* LWIP_NETIF_HOSTNAME */
+ return ERR_OK;
+}
+
+#if LWIP_IPV4
+/*
+ * Send an IPv4 packet on the given connection.
+ */
+static err_t ppp_netif_output_ip4(struct netif *netif, struct pbuf *pb, const ip4_addr_t *ipaddr) {
+ LWIP_UNUSED_ARG(ipaddr);
+#if PPP_IPV4_SUPPORT
+ return ppp_netif_output(netif, pb, PPP_IP);
+#else /* PPP_IPV4_SUPPORT */
+ LWIP_UNUSED_ARG(netif);
+ LWIP_UNUSED_ARG(pb);
+ return ERR_IF;
+#endif /* PPP_IPV4_SUPPORT */
+}
+#endif /* LWIP_IPV4 */
+
+#if PPP_IPV6_SUPPORT
+/*
+ * Send an IPv6 packet on the given connection.
+ */
+static err_t ppp_netif_output_ip6(struct netif *netif, struct pbuf *pb, const ip6_addr_t *ipaddr) {
+ LWIP_UNUSED_ARG(ipaddr);
+ return ppp_netif_output(netif, pb, PPP_IPV6);
+}
+#endif /* PPP_IPV6_SUPPORT */
+
+static err_t ppp_netif_output(struct netif *netif, struct pbuf *pb, u16_t protocol) {
+ ppp_pcb *pcb = (ppp_pcb*)netif->state;
+ err_t err;
+ struct pbuf *fpb = NULL;
+
+ /* Check that the link is up. */
+ if (0
+#if PPP_IPV4_SUPPORT
+ || (protocol == PPP_IP && !pcb->if4_up)
+#endif /* PPP_IPV4_SUPPORT */
+#if PPP_IPV6_SUPPORT
+ || (protocol == PPP_IPV6 && !pcb->if6_up)
+#endif /* PPP_IPV6_SUPPORT */
+ ) {
+ PPPDEBUG(LOG_ERR, ("ppp_netif_output[%d]: link not up\n", pcb->netif->num));
+ goto err_rte_drop;
+ }
+
+#if MPPE_SUPPORT
+ /* If MPPE is required, refuse any IP packet until we are able to crypt them. */
+ if (pcb->settings.require_mppe && pcb->ccp_transmit_method != CI_MPPE) {
+ PPPDEBUG(LOG_ERR, ("ppp_netif_output[%d]: MPPE required, not up\n", pcb->netif->num));
+ goto err_rte_drop;
+ }
+#endif /* MPPE_SUPPORT */
+
+#if VJ_SUPPORT
+ /*
+ * Attempt Van Jacobson header compression if VJ is configured and
+ * this is an IP packet.
+ */
+ if (protocol == PPP_IP && pcb->vj_enabled) {
+ switch (vj_compress_tcp(&pcb->vj_comp, &pb)) {
+ case TYPE_IP:
+ /* No change...
+ protocol = PPP_IP; */
+ break;
+ case TYPE_COMPRESSED_TCP:
+ /* vj_compress_tcp() returns a new allocated pbuf, indicate we should free
+ * our duplicated pbuf later */
+ fpb = pb;
+ protocol = PPP_VJC_COMP;
+ break;
+ case TYPE_UNCOMPRESSED_TCP:
+ /* vj_compress_tcp() returns a new allocated pbuf, indicate we should free
+ * our duplicated pbuf later */
+ fpb = pb;
+ protocol = PPP_VJC_UNCOMP;
+ break;
+ default:
+ PPPDEBUG(LOG_WARNING, ("ppp_netif_output[%d]: bad IP packet\n", pcb->netif->num));
+ LINK_STATS_INC(link.proterr);
+ LINK_STATS_INC(link.drop);
+ MIB2_STATS_NETIF_INC(pcb->netif, ifoutdiscards);
+ return ERR_VAL;
+ }
+ }
+#endif /* VJ_SUPPORT */
+
+#if CCP_SUPPORT
+ switch (pcb->ccp_transmit_method) {
+ case 0:
+ break; /* Don't compress */
+#if MPPE_SUPPORT
+ case CI_MPPE:
+ if ((err = mppe_compress(pcb, &pcb->mppe_comp, &pb, protocol)) != ERR_OK) {
+ LINK_STATS_INC(link.memerr);
+ LINK_STATS_INC(link.drop);
+ MIB2_STATS_NETIF_INC(netif, ifoutdiscards);
+ goto err;
+ }
+ /* if VJ compressor returned a new allocated pbuf, free it */
+ if (fpb) {
+ pbuf_free(fpb);
+ }
+ /* mppe_compress() returns a new allocated pbuf, indicate we should free
+ * our duplicated pbuf later */
+ fpb = pb;
+ protocol = PPP_COMP;
+ break;
+#endif /* MPPE_SUPPORT */
+ default:
+ PPPDEBUG(LOG_ERR, ("ppp_netif_output[%d]: bad CCP transmit method\n", pcb->netif->num));
+ goto err_rte_drop; /* Cannot really happen, we only negotiate what we are able to do */
+ }
+#endif /* CCP_SUPPORT */
+
+ err = pcb->link_cb->netif_output(pcb, pcb->link_ctx_cb, pb, protocol);
+ goto err;
+
+err_rte_drop:
+ err = ERR_RTE;
+ LINK_STATS_INC(link.rterr);
+ LINK_STATS_INC(link.drop);
+ MIB2_STATS_NETIF_INC(netif, ifoutdiscards);
+err:
+ if (fpb) {
+ pbuf_free(fpb);
+ }
+ return err;
+}
+
+/************************************/
+/*** PRIVATE FUNCTION DEFINITIONS ***/
+/************************************/
+
+/* Initialize the PPP subsystem. */
+int ppp_init(void)
+{
+#if PPPOS_SUPPORT
+ LWIP_MEMPOOL_INIT(PPPOS_PCB);
+#endif
+#if PPPOE_SUPPORT
+ LWIP_MEMPOOL_INIT(PPPOE_IF);
+#endif
+#if PPPOL2TP_SUPPORT
+ LWIP_MEMPOOL_INIT(PPPOL2TP_PCB);
+#endif
+#if LWIP_PPP_API && LWIP_MPU_COMPATIBLE
+ LWIP_MEMPOOL_INIT(PPPAPI_MSG);
+#endif
+
+ LWIP_MEMPOOL_INIT(PPP_PCB);
+
+ /*
+ * Initialize magic number generator now so that protocols may
+ * use magic numbers in initialization.
+ */
+ magic_init();
+
+ return 0;
+}
+
+/*
+ * Create a new PPP control block.
+ *
+ * This initializes the PPP control block but does not
+ * attempt to negotiate the LCP session.
+ *
+ * Return a new PPP connection control block pointer
+ * on success or a null pointer on failure.
+ */
+ppp_pcb *ppp_new(struct netif *pppif, const struct link_callbacks *callbacks, void *link_ctx_cb, ppp_link_status_cb_fn link_status_cb, void *ctx_cb) {
+ ppp_pcb *pcb;
+ const struct protent *protp;
+ int i;
+
+ /* PPP is single-threaded: without a callback,
+ * there is no way to know when the link is up. */
+ if (link_status_cb == NULL) {
+ return NULL;
+ }
+
+ pcb = (ppp_pcb*)LWIP_MEMPOOL_ALLOC(PPP_PCB);
+ if (pcb == NULL) {
+ return NULL;
+ }
+
+ memset(pcb, 0, sizeof(ppp_pcb));
+
+ /* default configuration */
+#if PAP_SUPPORT
+ pcb->settings.pap_timeout_time = UPAP_DEFTIMEOUT;
+ pcb->settings.pap_max_transmits = UPAP_DEFTRANSMITS;
+#if PPP_SERVER
+ pcb->settings.pap_req_timeout = UPAP_DEFREQTIME;
+#endif /* PPP_SERVER */
+#endif /* PAP_SUPPORT */
+
+#if CHAP_SUPPORT
+ pcb->settings.chap_timeout_time = CHAP_DEFTIMEOUT;
+ pcb->settings.chap_max_transmits = CHAP_DEFTRANSMITS;
+#if PPP_SERVER
+ pcb->settings.chap_rechallenge_time = CHAP_DEFRECHALLENGETIME;
+#endif /* PPP_SERVER */
+#endif /* CHAP_SUPPPORT */
+
+#if EAP_SUPPORT
+ pcb->settings.eap_req_time = EAP_DEFREQTIME;
+ pcb->settings.eap_allow_req = EAP_DEFALLOWREQ;
+#if PPP_SERVER
+ pcb->settings.eap_timeout_time = EAP_DEFTIMEOUT;
+ pcb->settings.eap_max_transmits = EAP_DEFTRANSMITS;
+#endif /* PPP_SERVER */
+#endif /* EAP_SUPPORT */
+
+ pcb->settings.lcp_loopbackfail = LCP_DEFLOOPBACKFAIL;
+ pcb->settings.lcp_echo_interval = LCP_ECHOINTERVAL;
+ pcb->settings.lcp_echo_fails = LCP_MAXECHOFAILS;
+
+ pcb->settings.fsm_timeout_time = FSM_DEFTIMEOUT;
+ pcb->settings.fsm_max_conf_req_transmits = FSM_DEFMAXCONFREQS;
+ pcb->settings.fsm_max_term_transmits = FSM_DEFMAXTERMREQS;
+ pcb->settings.fsm_max_nak_loops = FSM_DEFMAXNAKLOOPS;
+
+ pcb->netif = pppif;
+ MIB2_INIT_NETIF(pppif, snmp_ifType_ppp, 0);
+ if (!netif_add(pcb->netif,
+#if LWIP_IPV4
+ IP4_ADDR_ANY4, IP4_ADDR_BROADCAST, IP4_ADDR_ANY4,
+#endif /* LWIP_IPV4 */
+ (void *)pcb, ppp_netif_init_cb, NULL)) {
+ LWIP_MEMPOOL_FREE(PPP_PCB, pcb);
+ PPPDEBUG(LOG_ERR, ("ppp_new: netif_add failed\n"));
+ return NULL;
+ }
+
+ pcb->link_cb = callbacks;
+ pcb->link_ctx_cb = link_ctx_cb;
+ pcb->link_status_cb = link_status_cb;
+ pcb->ctx_cb = ctx_cb;
+
+ /*
+ * Initialize each protocol.
+ */
+ for (i = 0; (protp = protocols[i]) != NULL; ++i) {
+ (*protp->init)(pcb);
+ }
+
+ new_phase(pcb, PPP_PHASE_DEAD);
+ return pcb;
+}
+
+/** Initiate LCP open request */
+void ppp_start(ppp_pcb *pcb) {
+ PPPDEBUG(LOG_DEBUG, ("ppp_start[%d]\n", pcb->netif->num));
+
+ /* Clean data not taken care by anything else, mostly shared data. */
+#if PPP_STATS_SUPPORT
+ link_stats_valid = 0;
+#endif /* PPP_STATS_SUPPORT */
+#if MPPE_SUPPORT
+ pcb->mppe_keys_set = 0;
+ memset(&pcb->mppe_comp, 0, sizeof(pcb->mppe_comp));
+ memset(&pcb->mppe_decomp, 0, sizeof(pcb->mppe_decomp));
+#endif /* MPPE_SUPPORT */
+#if VJ_SUPPORT
+ vj_compress_init(&pcb->vj_comp);
+#endif /* VJ_SUPPORT */
+
+ /* Start protocol */
+ new_phase(pcb, PPP_PHASE_ESTABLISH);
+ lcp_open(pcb);
+ lcp_lowerup(pcb);
+ PPPDEBUG(LOG_DEBUG, ("ppp_start[%d]: finished\n", pcb->netif->num));
+}
+
+/** Called when link failed to setup */
+void ppp_link_failed(ppp_pcb *pcb) {
+ PPPDEBUG(LOG_DEBUG, ("ppp_link_failed[%d]\n", pcb->netif->num));
+ new_phase(pcb, PPP_PHASE_DEAD);
+ pcb->err_code = PPPERR_OPEN;
+ pcb->link_status_cb(pcb, pcb->err_code, pcb->ctx_cb);
+}
+
+/** Called when link is normally down (i.e. it was asked to end) */
+void ppp_link_end(ppp_pcb *pcb) {
+ PPPDEBUG(LOG_DEBUG, ("ppp_link_end[%d]\n", pcb->netif->num));
+ new_phase(pcb, PPP_PHASE_DEAD);
+ if (pcb->err_code == PPPERR_NONE) {
+ pcb->err_code = PPPERR_CONNECT;
+ }
+ pcb->link_status_cb(pcb, pcb->err_code, pcb->ctx_cb);
+}
+
+/*
+ * Pass the processed input packet to the appropriate handler.
+ * This function and all handlers run in the context of the tcpip_thread
+ */
+void ppp_input(ppp_pcb *pcb, struct pbuf *pb) {
+ u16_t protocol;
+#if PPP_DEBUG && PPP_PROTOCOLNAME
+ const char *pname;
+#endif /* PPP_DEBUG && PPP_PROTOCOLNAME */
+
+ magic_randomize();
+
+ if (pb->len < 2) {
+ PPPDEBUG(LOG_ERR, ("ppp_input[%d]: packet too short\n", pcb->netif->num));
+ goto drop;
+ }
+ protocol = (((u8_t *)pb->payload)[0] << 8) | ((u8_t*)pb->payload)[1];
+
+#if PRINTPKT_SUPPORT
+ ppp_dump_packet(pcb, "rcvd", (unsigned char *)pb->payload, pb->len);
+#endif /* PRINTPKT_SUPPORT */
+
+ pbuf_header(pb, -(s16_t)sizeof(protocol));
+
+ LINK_STATS_INC(link.recv);
+ MIB2_STATS_NETIF_INC(pcb->netif, ifinucastpkts);
+ MIB2_STATS_NETIF_ADD(pcb->netif, ifinoctets, pb->tot_len);
+
+ /*
+ * Toss all non-LCP packets unless LCP is OPEN.
+ */
+ if (protocol != PPP_LCP && pcb->lcp_fsm.state != PPP_FSM_OPENED) {
+ ppp_dbglog("Discarded non-LCP packet when LCP not open");
+ goto drop;
+ }
+
+ /*
+ * Until we get past the authentication phase, toss all packets
+ * except LCP, LQR and authentication packets.
+ */
+ if (pcb->phase <= PPP_PHASE_AUTHENTICATE
+ && !(protocol == PPP_LCP
+#if LQR_SUPPORT
+ || protocol == PPP_LQR
+#endif /* LQR_SUPPORT */
+#if PAP_SUPPORT
+ || protocol == PPP_PAP
+#endif /* PAP_SUPPORT */
+#if CHAP_SUPPORT
+ || protocol == PPP_CHAP
+#endif /* CHAP_SUPPORT */
+#if EAP_SUPPORT
+ || protocol == PPP_EAP
+#endif /* EAP_SUPPORT */
+ )) {
+ ppp_dbglog("discarding proto 0x%x in phase %d", protocol, pcb->phase);
+ goto drop;
+ }
+
+#if CCP_SUPPORT
+#if MPPE_SUPPORT
+ /*
+ * MPPE is required and unencrypted data has arrived (this
+ * should never happen!). We should probably drop the link if
+ * the protocol is in the range of what should be encrypted.
+ * At the least, we drop this packet.
+ */
+ if (pcb->settings.require_mppe && protocol != PPP_COMP && protocol < 0x8000) {
+ PPPDEBUG(LOG_ERR, ("ppp_input[%d]: MPPE required, received unencrypted data!\n", pcb->netif->num));
+ goto drop;
+ }
+#endif /* MPPE_SUPPORT */
+
+ if (protocol == PPP_COMP) {
+ u8_t *pl;
+
+ switch (pcb->ccp_receive_method) {
+#if MPPE_SUPPORT
+ case CI_MPPE:
+ if (mppe_decompress(pcb, &pcb->mppe_decomp, &pb) != ERR_OK) {
+ goto drop;
+ }
+ break;
+#endif /* MPPE_SUPPORT */
+ default:
+ PPPDEBUG(LOG_ERR, ("ppp_input[%d]: bad CCP receive method\n", pcb->netif->num));
+ goto drop; /* Cannot really happen, we only negotiate what we are able to do */
+ }
+
+ /* Assume no PFC */
+ if (pb->len < 2) {
+ goto drop;
+ }
+
+ /* Extract and hide protocol (do PFC decompression if necessary) */
+ pl = (u8_t*)pb->payload;
+ if (pl[0] & 0x01) {
+ protocol = pl[0];
+ pbuf_header(pb, -(s16_t)1);
+ } else {
+ protocol = (pl[0] << 8) | pl[1];
+ pbuf_header(pb, -(s16_t)2);
+ }
+ }
+#endif /* CCP_SUPPORT */
+
+ switch(protocol) {
+
+#if PPP_IPV4_SUPPORT
+ case PPP_IP: /* Internet Protocol */
+ PPPDEBUG(LOG_INFO, ("ppp_input[%d]: ip in pbuf len=%d\n", pcb->netif->num, pb->tot_len));
+ ip4_input(pb, pcb->netif);
+ return;
+#endif /* PPP_IPV4_SUPPORT */
+
+#if PPP_IPV6_SUPPORT
+ case PPP_IPV6: /* Internet Protocol Version 6 */
+ PPPDEBUG(LOG_INFO, ("ppp_input[%d]: ip6 in pbuf len=%d\n", pcb->netif->num, pb->tot_len));
+ ip6_input(pb, pcb->netif);
+ return;
+#endif /* PPP_IPV6_SUPPORT */
+
+#if VJ_SUPPORT
+ case PPP_VJC_COMP: /* VJ compressed TCP */
+ /*
+ * Clip off the VJ header and prepend the rebuilt TCP/IP header and
+ * pass the result to IP.
+ */
+ PPPDEBUG(LOG_INFO, ("ppp_input[%d]: vj_comp in pbuf len=%d\n", pcb->netif->num, pb->tot_len));
+ if (pcb->vj_enabled && vj_uncompress_tcp(&pb, &pcb->vj_comp) >= 0) {
+ ip4_input(pb, pcb->netif);
+ return;
+ }
+ /* Something's wrong so drop it. */
+ PPPDEBUG(LOG_WARNING, ("ppp_input[%d]: Dropping VJ compressed\n", pcb->netif->num));
+ break;
+
+ case PPP_VJC_UNCOMP: /* VJ uncompressed TCP */
+ /*
+ * Process the TCP/IP header for VJ header compression and then pass
+ * the packet to IP.
+ */
+ PPPDEBUG(LOG_INFO, ("ppp_input[%d]: vj_un in pbuf len=%d\n", pcb->netif->num, pb->tot_len));
+ if (pcb->vj_enabled && vj_uncompress_uncomp(pb, &pcb->vj_comp) >= 0) {
+ ip4_input(pb, pcb->netif);
+ return;
+ }
+ /* Something's wrong so drop it. */
+ PPPDEBUG(LOG_WARNING, ("ppp_input[%d]: Dropping VJ uncompressed\n", pcb->netif->num));
+ break;
+#endif /* VJ_SUPPORT */
+
+ default: {
+ int i;
+ const struct protent *protp;
+
+ /*
+ * Upcall the proper protocol input routine.
+ */
+ for (i = 0; (protp = protocols[i]) != NULL; ++i) {
+ if (protp->protocol == protocol) {
+ pb = ppp_singlebuf(pb);
+ (*protp->input)(pcb, (u8_t*)pb->payload, pb->len);
+ goto out;
+ }
+#if 0 /* UNUSED
+ *
+ * This is actually a (hacked?) way for the Linux kernel to pass a data
+ * packet to pppd. pppd in normal condition only do signaling
+ * (LCP, PAP, CHAP, IPCP, ...) and does not handle any data packet at all.
+ *
+ * We don't even need this interface, which is only there because of PPP
+ * interface limitation between Linux kernel and pppd. For MPPE, which uses
+ * CCP to negotiate although it is not really a (de)compressor, we added
+ * ccp_resetrequest() in CCP and MPPE input data flow is calling either
+ * ccp_resetrequest() or lcp_close() if the issue is, respectively, non-fatal
+ * or fatal, this is what ccp_datainput() really do.
+ */
+ if (protocol == (protp->protocol & ~0x8000)
+ && protp->datainput != NULL) {
+ (*protp->datainput)(pcb, pb->payload, pb->len);
+ goto out;
+ }
+#endif /* UNUSED */
+ }
+
+#if PPP_DEBUG
+#if PPP_PROTOCOLNAME
+ pname = protocol_name(protocol);
+ if (pname != NULL) {
+ ppp_warn("Unsupported protocol '%s' (0x%x) received", pname, protocol);
+ } else
+#endif /* PPP_PROTOCOLNAME */
+ ppp_warn("Unsupported protocol 0x%x received", protocol);
+#endif /* PPP_DEBUG */
+ pbuf_header(pb, (s16_t)sizeof(protocol));
+ lcp_sprotrej(pcb, (u8_t*)pb->payload, pb->len);
+ }
+ break;
+ }
+
+drop:
+ LINK_STATS_INC(link.drop);
+ MIB2_STATS_NETIF_INC(pcb->netif, ifindiscards);
+
+out:
+ pbuf_free(pb);
+}
+
+/* merge a pbuf chain into one pbuf */
+struct pbuf *ppp_singlebuf(struct pbuf *p) {
+ struct pbuf *q, *b;
+ u8_t *pl;
+
+ if(p->tot_len == p->len) {
+ return p;
+ }
+
+ q = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM);
+ if(!q) {
+ PPPDEBUG(LOG_ERR,
+ ("ppp_singlebuf: unable to alloc new buf (%d)\n", p->tot_len));
+ return p; /* live dangerously */
+ }
+
+ for(b = p, pl = (u8_t*)q->payload; b != NULL; b = b->next) {
+ MEMCPY(pl, b->payload, b->len);
+ pl += b->len;
+ }
+
+ pbuf_free(p);
+
+ return q;
+}
+
+/*
+ * Write a pbuf to a ppp link, only used from PPP functions
+ * to send PPP packets.
+ *
+ * IPv4 and IPv6 packets from lwIP are sent, respectively,
+ * with ppp_netif_output_ip4() and ppp_netif_output_ip6()
+ * functions (which are callbacks of the netif PPP interface).
+ */
+err_t ppp_write(ppp_pcb *pcb, struct pbuf *p) {
+#if PRINTPKT_SUPPORT
+ ppp_dump_packet(pcb, "sent", (unsigned char *)p->payload+2, p->len-2);
+#endif /* PRINTPKT_SUPPORT */
+ return pcb->link_cb->write(pcb, pcb->link_ctx_cb, p);
+}
+
+void ppp_link_terminated(ppp_pcb *pcb) {
+ PPPDEBUG(LOG_DEBUG, ("ppp_link_terminated[%d]\n", pcb->netif->num));
+ pcb->link_cb->disconnect(pcb, pcb->link_ctx_cb);
+ PPPDEBUG(LOG_DEBUG, ("ppp_link_terminated[%d]: finished.\n", pcb->netif->num));
+}
+
+
+/************************************************************************
+ * Functions called by various PPP subsystems to configure
+ * the PPP interface or change the PPP phase.
+ */
+
+/*
+ * new_phase - signal the start of a new phase of pppd's operation.
+ */
+void new_phase(ppp_pcb *pcb, int p) {
+ pcb->phase = p;
+ PPPDEBUG(LOG_DEBUG, ("ppp phase changed[%d]: phase=%d\n", pcb->netif->num, pcb->phase));
+#if PPP_NOTIFY_PHASE
+ if (pcb->notify_phase_cb != NULL) {
+ pcb->notify_phase_cb(pcb, p, pcb->ctx_cb);
+ }
+#endif /* PPP_NOTIFY_PHASE */
+}
+
+/*
+ * ppp_send_config - configure the transmit-side characteristics of
+ * the ppp interface.
+ */
+int ppp_send_config(ppp_pcb *pcb, int mtu, u32_t accm, int pcomp, int accomp) {
+ LWIP_UNUSED_ARG(mtu);
+ /* pcb->mtu = mtu; -- set correctly with netif_set_mtu */
+
+ if (pcb->link_cb->send_config) {
+ pcb->link_cb->send_config(pcb, pcb->link_ctx_cb, accm, pcomp, accomp);
+ }
+
+ PPPDEBUG(LOG_INFO, ("ppp_send_config[%d]\n", pcb->netif->num) );
+ return 0;
+}
+
+/*
+ * ppp_recv_config - configure the receive-side characteristics of
+ * the ppp interface.
+ */
+int ppp_recv_config(ppp_pcb *pcb, int mru, u32_t accm, int pcomp, int accomp) {
+ LWIP_UNUSED_ARG(mru);
+
+ if (pcb->link_cb->recv_config) {
+ pcb->link_cb->recv_config(pcb, pcb->link_ctx_cb, accm, pcomp, accomp);
+ }
+
+ PPPDEBUG(LOG_INFO, ("ppp_recv_config[%d]\n", pcb->netif->num));
+ return 0;
+}
+
+#if PPP_IPV4_SUPPORT
+/*
+ * sifaddr - Config the interface IP addresses and netmask.
+ */
+int sifaddr(ppp_pcb *pcb, u32_t our_adr, u32_t his_adr, u32_t netmask) {
+ ip4_addr_t ip, nm, gw;
+
+ ip4_addr_set_u32(&ip, our_adr);
+ ip4_addr_set_u32(&nm, netmask);
+ ip4_addr_set_u32(&gw, his_adr);
+ netif_set_addr(pcb->netif, &ip, &nm, &gw);
+ return 1;
+}
+
+/********************************************************************
+ *
+ * cifaddr - Clear the interface IP addresses, and delete routes
+ * through the interface if possible.
+ */
+int cifaddr(ppp_pcb *pcb, u32_t our_adr, u32_t his_adr) {
+ LWIP_UNUSED_ARG(our_adr);
+ LWIP_UNUSED_ARG(his_adr);
+
+ netif_set_addr(pcb->netif, IP4_ADDR_ANY4, IP4_ADDR_BROADCAST, IP4_ADDR_ANY4);
+ return 1;
+}
+
+#if 0 /* UNUSED - PROXY ARP */
+/********************************************************************
+ *
+ * sifproxyarp - Make a proxy ARP entry for the peer.
+ */
+
+int sifproxyarp(ppp_pcb *pcb, u32_t his_adr) {
+ LWIP_UNUSED_ARG(pcb);
+ LWIP_UNUSED_ARG(his_adr);
+ return 0;
+}
+
+/********************************************************************
+ *
+ * cifproxyarp - Delete the proxy ARP entry for the peer.
+ */
+
+int cifproxyarp(ppp_pcb *pcb, u32_t his_adr) {
+ LWIP_UNUSED_ARG(pcb);
+ LWIP_UNUSED_ARG(his_adr);
+ return 0;
+}
+#endif /* UNUSED - PROXY ARP */
+
+#if LWIP_DNS
+/*
+ * sdns - Config the DNS servers
+ */
+int sdns(ppp_pcb *pcb, u32_t ns1, u32_t ns2) {
+ ip_addr_t ns;
+ LWIP_UNUSED_ARG(pcb);
+
+ ip_addr_set_ip4_u32(&ns, ns1);
+ dns_setserver(0, &ns);
+ ip_addr_set_ip4_u32(&ns, ns2);
+ dns_setserver(1, &ns);
+ return 1;
+}
+
+/********************************************************************
+ *
+ * cdns - Clear the DNS servers
+ */
+int cdns(ppp_pcb *pcb, u32_t ns1, u32_t ns2) {
+ const ip_addr_t *nsa;
+ ip_addr_t nsb;
+ LWIP_UNUSED_ARG(pcb);
+
+ nsa = dns_getserver(0);
+ ip_addr_set_ip4_u32(&nsb, ns1);
+ if (ip_addr_cmp(nsa, &nsb)) {
+ dns_setserver(0, IP_ADDR_ANY);
+ }
+ nsa = dns_getserver(1);
+ ip_addr_set_ip4_u32(&nsb, ns2);
+ if (ip_addr_cmp(nsa, &nsb)) {
+ dns_setserver(1, IP_ADDR_ANY);
+ }
+ return 1;
+}
+#endif /* LWIP_DNS */
+
+#if VJ_SUPPORT
+/********************************************************************
+ *
+ * sifvjcomp - config tcp header compression
+ */
+int sifvjcomp(ppp_pcb *pcb, int vjcomp, int cidcomp, int maxcid) {
+ pcb->vj_enabled = vjcomp;
+ pcb->vj_comp.compressSlot = cidcomp;
+ pcb->vj_comp.maxSlotIndex = maxcid;
+ PPPDEBUG(LOG_INFO, ("sifvjcomp[%d]: VJ compress enable=%d slot=%d max slot=%d\n",
+ pcb->netif->num, vjcomp, cidcomp, maxcid));
+ return 0;
+}
+#endif /* VJ_SUPPORT */
+
+/*
+ * sifup - Config the interface up and enable IP packets to pass.
+ */
+int sifup(ppp_pcb *pcb) {
+ pcb->if4_up = 1;
+ pcb->err_code = PPPERR_NONE;
+ netif_set_link_up(pcb->netif);
+
+ PPPDEBUG(LOG_DEBUG, ("sifup[%d]: err_code=%d\n", pcb->netif->num, pcb->err_code));
+ pcb->link_status_cb(pcb, pcb->err_code, pcb->ctx_cb);
+ return 1;
+}
+
+/********************************************************************
+ *
+ * sifdown - Disable the indicated protocol and config the interface
+ * down if there are no remaining protocols.
+ */
+int sifdown(ppp_pcb *pcb) {
+
+ pcb->if4_up = 0;
+
+ if (1
+#if PPP_IPV6_SUPPORT
+ /* set the interface down if IPv6 is down as well */
+ && !pcb->if6_up
+#endif /* PPP_IPV6_SUPPORT */
+ ) {
+ /* make sure the netif link callback is called */
+ netif_set_link_down(pcb->netif);
+ }
+ PPPDEBUG(LOG_DEBUG, ("sifdown[%d]: err_code=%d\n", pcb->netif->num, pcb->err_code));
+ return 1;
+}
+
+/********************************************************************
+ *
+ * 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 get_mask(u32_t addr) {
+#if 0
+ u32_t mask, nmask;
+
+ addr = lwip_htonl(addr);
+ if (IP_CLASSA(addr)) { /* determine network mask for address class */
+ nmask = IP_CLASSA_NET;
+ } else if (IP_CLASSB(addr)) {
+ nmask = IP_CLASSB_NET;
+ } else {
+ nmask = IP_CLASSC_NET;
+ }
+
+ /* class D nets are disallowed by bad_ip_adrs */
+ mask = PP_HTONL(0xffffff00UL) | lwip_htonl(nmask);
+
+ /* XXX
+ * Scan through the system's network interfaces.
+ * Get each netmask and OR them into our mask.
+ */
+ /* return mask; */
+ return mask;
+#endif /* 0 */
+ LWIP_UNUSED_ARG(addr);
+ return IPADDR_BROADCAST;
+}
+#endif /* PPP_IPV4_SUPPORT */
+
+#if PPP_IPV6_SUPPORT
+#define IN6_LLADDR_FROM_EUI64(ip6, eui64) do { \
+ ip6.addr[0] = PP_HTONL(0xfe800000); \
+ ip6.addr[1] = 0; \
+ eui64_copy(eui64, ip6.addr[2]); \
+ } while (0)
+
+/********************************************************************
+ *
+ * sif6addr - Config the interface with an IPv6 link-local address
+ */
+int sif6addr(ppp_pcb *pcb, eui64_t our_eui64, eui64_t his_eui64) {
+ ip6_addr_t ip6;
+ LWIP_UNUSED_ARG(his_eui64);
+
+ IN6_LLADDR_FROM_EUI64(ip6, our_eui64);
+ netif_ip6_addr_set(pcb->netif, 0, &ip6);
+ netif_ip6_addr_set_state(pcb->netif, 0, IP6_ADDR_PREFERRED);
+ /* FIXME: should we add an IPv6 static neighbor using his_eui64 ? */
+ return 1;
+}
+
+/********************************************************************
+ *
+ * cif6addr - Remove IPv6 address from interface
+ */
+int cif6addr(ppp_pcb *pcb, eui64_t our_eui64, eui64_t his_eui64) {
+ LWIP_UNUSED_ARG(our_eui64);
+ LWIP_UNUSED_ARG(his_eui64);
+
+ netif_ip6_addr_set(pcb->netif, 0, IP6_ADDR_ANY6);
+ netif_ip6_addr_set_state(pcb->netif, 0, IP6_ADDR_INVALID);
+ return 1;
+}
+
+/*
+ * sif6up - Config the interface up and enable IPv6 packets to pass.
+ */
+int sif6up(ppp_pcb *pcb) {
+
+ pcb->if6_up = 1;
+ pcb->err_code = PPPERR_NONE;
+ netif_set_link_up(pcb->netif);
+
+ PPPDEBUG(LOG_DEBUG, ("sif6up[%d]: err_code=%d\n", pcb->netif->num, pcb->err_code));
+ pcb->link_status_cb(pcb, pcb->err_code, pcb->ctx_cb);
+ return 1;
+}
+
+/********************************************************************
+ *
+ * sif6down - Disable the indicated protocol and config the interface
+ * down if there are no remaining protocols.
+ */
+int sif6down(ppp_pcb *pcb) {
+
+ pcb->if6_up = 0;
+
+ if (1
+#if PPP_IPV4_SUPPORT
+ /* set the interface down if IPv4 is down as well */
+ && !pcb->if4_up
+#endif /* PPP_IPV4_SUPPORT */
+ ) {
+ /* make sure the netif link callback is called */
+ netif_set_link_down(pcb->netif);
+ }
+ PPPDEBUG(LOG_DEBUG, ("sif6down[%d]: err_code=%d\n", pcb->netif->num, pcb->err_code));
+ return 1;
+}
+#endif /* PPP_IPV6_SUPPORT */
+
+#if DEMAND_SUPPORT
+/*
+ * sifnpmode - Set the mode for handling packets for a given NP.
+ */
+int sifnpmode(ppp_pcb *pcb, int proto, enum NPmode mode) {
+ LWIP_UNUSED_ARG(pcb);
+ LWIP_UNUSED_ARG(proto);
+ LWIP_UNUSED_ARG(mode);
+ return 0;
+}
+#endif /* DEMAND_SUPPORT */
+
+/*
+ * netif_set_mtu - set the MTU on the PPP network interface.
+ */
+void netif_set_mtu(ppp_pcb *pcb, int mtu) {
+
+ pcb->netif->mtu = mtu;
+ PPPDEBUG(LOG_INFO, ("netif_set_mtu[%d]: mtu=%d\n", pcb->netif->num, mtu));
+}
+
+/*
+ * netif_get_mtu - get PPP interface MTU
+ */
+int netif_get_mtu(ppp_pcb *pcb) {
+
+ return pcb->netif->mtu;
+}
+
+#if CCP_SUPPORT
+#if 0 /* unused */
+/*
+ * ccp_test - whether a given compression method is acceptable for use.
+ */
+int
+ccp_test(ppp_pcb *pcb, u_char *opt_ptr, int opt_len, int for_transmit)
+{
+ LWIP_UNUSED_ARG(pcb);
+ LWIP_UNUSED_ARG(opt_ptr);
+ LWIP_UNUSED_ARG(opt_len);
+ LWIP_UNUSED_ARG(for_transmit);
+ return -1;
+}
+#endif /* unused */
+
+/*
+ * ccp_set - inform about the current state of CCP.
+ */
+void
+ccp_set(ppp_pcb *pcb, u8_t isopen, u8_t isup, u8_t receive_method, u8_t transmit_method)
+{
+ LWIP_UNUSED_ARG(isopen);
+ LWIP_UNUSED_ARG(isup);
+ pcb->ccp_receive_method = receive_method;
+ pcb->ccp_transmit_method = transmit_method;
+ PPPDEBUG(LOG_DEBUG, ("ccp_set[%d]: is_open=%d, is_up=%d, receive_method=%u, transmit_method=%u\n",
+ pcb->netif->num, isopen, isup, receive_method, transmit_method));
+}
+
+void
+ccp_reset_comp(ppp_pcb *pcb)
+{
+ switch (pcb->ccp_transmit_method) {
+#if MPPE_SUPPORT
+ case CI_MPPE:
+ mppe_comp_reset(pcb, &pcb->mppe_comp);
+ break;
+#endif /* MPPE_SUPPORT */
+ default:
+ break;
+ }
+}
+
+void
+ccp_reset_decomp(ppp_pcb *pcb)
+{
+ switch (pcb->ccp_receive_method) {
+#if MPPE_SUPPORT
+ case CI_MPPE:
+ mppe_decomp_reset(pcb, &pcb->mppe_decomp);
+ break;
+#endif /* MPPE_SUPPORT */
+ default:
+ break;
+ }
+}
+
+#if 0 /* unused */
+/*
+ * 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(ppp_pcb *pcb)
+{
+ LWIP_UNUSED_ARG(pcb);
+ return 1;
+}
+#endif /* unused */
+#endif /* CCP_SUPPORT */
+
+#if PPP_IDLETIMELIMIT
+/********************************************************************
+ *
+ * get_idle_time - return how long the link has been idle.
+ */
+int get_idle_time(ppp_pcb *pcb, struct ppp_idle *ip) {
+ /* FIXME: add idle time support and make it optional */
+ LWIP_UNUSED_ARG(pcb);
+ LWIP_UNUSED_ARG(ip);
+ return 1;
+}
+#endif /* PPP_IDLETIMELIMIT */
+
+#if DEMAND_SUPPORT
+/********************************************************************
+ *
+ * get_loop_output - get outgoing packets from the ppp device,
+ * and detect when we want to bring the real link up.
+ * Return value is 1 if we need to bring up the link, 0 otherwise.
+ */
+int get_loop_output(void) {
+ return 0;
+}
+#endif /* DEMAND_SUPPORT */
+
+#if PPP_PROTOCOLNAME
+/* List of protocol names, to make our messages a little more informative. */
+struct protocol_list {
+ u_short proto;
+ const char *name;
+} const protocol_list[] = {
+ { 0x21, "IP" },
+ { 0x23, "OSI Network Layer" },
+ { 0x25, "Xerox NS IDP" },
+ { 0x27, "DECnet Phase IV" },
+ { 0x29, "Appletalk" },
+ { 0x2b, "Novell IPX" },
+ { 0x2d, "VJ compressed TCP/IP" },
+ { 0x2f, "VJ uncompressed TCP/IP" },
+ { 0x31, "Bridging PDU" },
+ { 0x33, "Stream Protocol ST-II" },
+ { 0x35, "Banyan Vines" },
+ { 0x39, "AppleTalk EDDP" },
+ { 0x3b, "AppleTalk SmartBuffered" },
+ { 0x3d, "Multi-Link" },
+ { 0x3f, "NETBIOS Framing" },
+ { 0x41, "Cisco Systems" },
+ { 0x43, "Ascom Timeplex" },
+ { 0x45, "Fujitsu Link Backup and Load Balancing (LBLB)" },
+ { 0x47, "DCA Remote Lan" },
+ { 0x49, "Serial Data Transport Protocol (PPP-SDTP)" },
+ { 0x4b, "SNA over 802.2" },
+ { 0x4d, "SNA" },
+ { 0x4f, "IP6 Header Compression" },
+ { 0x51, "KNX Bridging Data" },
+ { 0x53, "Encryption" },
+ { 0x55, "Individual Link Encryption" },
+ { 0x57, "IPv6" },
+ { 0x59, "PPP Muxing" },
+ { 0x5b, "Vendor-Specific Network Protocol" },
+ { 0x61, "RTP IPHC Full Header" },
+ { 0x63, "RTP IPHC Compressed TCP" },
+ { 0x65, "RTP IPHC Compressed non-TCP" },
+ { 0x67, "RTP IPHC Compressed UDP 8" },
+ { 0x69, "RTP IPHC Compressed RTP 8" },
+ { 0x6f, "Stampede Bridging" },
+ { 0x73, "MP+" },
+ { 0xc1, "NTCITS IPI" },
+ { 0xfb, "single-link compression" },
+ { 0xfd, "Compressed Datagram" },
+ { 0x0201, "802.1d Hello Packets" },
+ { 0x0203, "IBM Source Routing BPDU" },
+ { 0x0205, "DEC LANBridge100 Spanning Tree" },
+ { 0x0207, "Cisco Discovery Protocol" },
+ { 0x0209, "Netcs Twin Routing" },
+ { 0x020b, "STP - Scheduled Transfer Protocol" },
+ { 0x020d, "EDP - Extreme Discovery Protocol" },
+ { 0x0211, "Optical Supervisory Channel Protocol" },
+ { 0x0213, "Optical Supervisory Channel Protocol" },
+ { 0x0231, "Luxcom" },
+ { 0x0233, "Sigma Network Systems" },
+ { 0x0235, "Apple Client Server Protocol" },
+ { 0x0281, "MPLS Unicast" },
+ { 0x0283, "MPLS Multicast" },
+ { 0x0285, "IEEE p1284.4 standard - data packets" },
+ { 0x0287, "ETSI TETRA Network Protocol Type 1" },
+ { 0x0289, "Multichannel Flow Treatment Protocol" },
+ { 0x2063, "RTP IPHC Compressed TCP No Delta" },
+ { 0x2065, "RTP IPHC Context State" },
+ { 0x2067, "RTP IPHC Compressed UDP 16" },
+ { 0x2069, "RTP IPHC Compressed RTP 16" },
+ { 0x4001, "Cray Communications Control Protocol" },
+ { 0x4003, "CDPD Mobile Network Registration Protocol" },
+ { 0x4005, "Expand accelerator protocol" },
+ { 0x4007, "ODSICP NCP" },
+ { 0x4009, "DOCSIS DLL" },
+ { 0x400B, "Cetacean Network Detection Protocol" },
+ { 0x4021, "Stacker LZS" },
+ { 0x4023, "RefTek Protocol" },
+ { 0x4025, "Fibre Channel" },
+ { 0x4027, "EMIT Protocols" },
+ { 0x405b, "Vendor-Specific Protocol (VSP)" },
+ { 0x8021, "Internet Protocol Control Protocol" },
+ { 0x8023, "OSI Network Layer Control Protocol" },
+ { 0x8025, "Xerox NS IDP Control Protocol" },
+ { 0x8027, "DECnet Phase IV Control Protocol" },
+ { 0x8029, "Appletalk Control Protocol" },
+ { 0x802b, "Novell IPX Control Protocol" },
+ { 0x8031, "Bridging NCP" },
+ { 0x8033, "Stream Protocol Control Protocol" },
+ { 0x8035, "Banyan Vines Control Protocol" },
+ { 0x803d, "Multi-Link Control Protocol" },
+ { 0x803f, "NETBIOS Framing Control Protocol" },
+ { 0x8041, "Cisco Systems Control Protocol" },
+ { 0x8043, "Ascom Timeplex" },
+ { 0x8045, "Fujitsu LBLB Control Protocol" },
+ { 0x8047, "DCA Remote Lan Network Control Protocol (RLNCP)" },
+ { 0x8049, "Serial Data Control Protocol (PPP-SDCP)" },
+ { 0x804b, "SNA over 802.2 Control Protocol" },
+ { 0x804d, "SNA Control Protocol" },
+ { 0x804f, "IP6 Header Compression Control Protocol" },
+ { 0x8051, "KNX Bridging Control Protocol" },
+ { 0x8053, "Encryption Control Protocol" },
+ { 0x8055, "Individual Link Encryption Control Protocol" },
+ { 0x8057, "IPv6 Control Protocol" },
+ { 0x8059, "PPP Muxing Control Protocol" },
+ { 0x805b, "Vendor-Specific Network Control Protocol (VSNCP)" },
+ { 0x806f, "Stampede Bridging Control Protocol" },
+ { 0x8073, "MP+ Control Protocol" },
+ { 0x80c1, "NTCITS IPI Control Protocol" },
+ { 0x80fb, "Single Link Compression Control Protocol" },
+ { 0x80fd, "Compression Control Protocol" },
+ { 0x8207, "Cisco Discovery Protocol Control" },
+ { 0x8209, "Netcs Twin Routing" },
+ { 0x820b, "STP - Control Protocol" },
+ { 0x820d, "EDPCP - Extreme Discovery Protocol Ctrl Prtcl" },
+ { 0x8235, "Apple Client Server Protocol Control" },
+ { 0x8281, "MPLSCP" },
+ { 0x8285, "IEEE p1284.4 standard - Protocol Control" },
+ { 0x8287, "ETSI TETRA TNP1 Control Protocol" },
+ { 0x8289, "Multichannel Flow Treatment Protocol" },
+ { 0xc021, "Link Control Protocol" },
+ { 0xc023, "Password Authentication Protocol" },
+ { 0xc025, "Link Quality Report" },
+ { 0xc027, "Shiva Password Authentication Protocol" },
+ { 0xc029, "CallBack Control Protocol (CBCP)" },
+ { 0xc02b, "BACP Bandwidth Allocation Control Protocol" },
+ { 0xc02d, "BAP" },
+ { 0xc05b, "Vendor-Specific Authentication Protocol (VSAP)" },
+ { 0xc081, "Container Control Protocol" },
+ { 0xc223, "Challenge Handshake Authentication Protocol" },
+ { 0xc225, "RSA Authentication Protocol" },
+ { 0xc227, "Extensible Authentication Protocol" },
+ { 0xc229, "Mitsubishi Security Info Exch Ptcl (SIEP)" },
+ { 0xc26f, "Stampede Bridging Authorization Protocol" },
+ { 0xc281, "Proprietary Authentication Protocol" },
+ { 0xc283, "Proprietary Authentication Protocol" },
+ { 0xc481, "Proprietary Node ID Authentication Protocol" },
+ { 0, NULL },
+};
+
+/*
+ * protocol_name - find a name for a PPP protocol.
+ */
+const char * protocol_name(int proto) {
+ const struct protocol_list *lp;
+
+ for (lp = protocol_list; lp->proto != 0; ++lp) {
+ if (proto == lp->proto) {
+ return lp->name;
+ }
+ }
+ return NULL;
+}
+#endif /* PPP_PROTOCOLNAME */
+
+#if PPP_STATS_SUPPORT
+
+/* ---- Note on PPP Stats support ----
+ *
+ * The one willing link stats support should add the get_ppp_stats()
+ * to fetch statistics from lwIP.
+ */
+
+/*
+ * reset_link_stats - "reset" stats when link goes up.
+ */
+void reset_link_stats(int u) {
+ if (!get_ppp_stats(u, &old_link_stats)) {
+ return;
+ }
+ gettimeofday(&start_time, NULL);
+}
+
+/*
+ * update_link_stats - get stats at link termination.
+ */
+void update_link_stats(int u) {
+ struct timeval now;
+ char numbuf[32];
+
+ if (!get_ppp_stats(u, &link_stats) || gettimeofday(&now, NULL) < 0) {
+ return;
+ }
+ link_connect_time = now.tv_sec - start_time.tv_sec;
+ link_stats_valid = 1;
+
+ link_stats.bytes_in -= old_link_stats.bytes_in;
+ link_stats.bytes_out -= old_link_stats.bytes_out;
+ link_stats.pkts_in -= old_link_stats.pkts_in;
+ link_stats.pkts_out -= old_link_stats.pkts_out;
+}
+
+void print_link_stats() {
+ /*
+ * Print connect time and statistics.
+ */
+ if (link_stats_valid) {
+ int t = (link_connect_time + 5) / 6; /* 1/10ths of minutes */
+ info("Connect time %d.%d minutes.", t/10, t%10);
+ info("Sent %u bytes, received %u bytes.", link_stats.bytes_out, link_stats.bytes_in);
+ link_stats_valid = 0;
+ }
+}
+#endif /* PPP_STATS_SUPPORT */
+
+#endif /* PPP_SUPPORT */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/pppapi.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/pppapi.c
new file mode 100644
index 0000000..947f7ba
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/pppapi.c
@@ -0,0 +1,427 @@
+/**
+ * @file
+ * Point To Point Protocol Sequential API module
+ *
+ */
+
+/*
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ */
+
+#include "netif/ppp/ppp_opts.h"
+
+#if LWIP_PPP_API /* don't build if not configured for use in lwipopts.h */
+
+#include "netif/ppp/pppapi.h"
+#include "lwip/priv/tcpip_priv.h"
+#include "netif/ppp/pppoe.h"
+#include "netif/ppp/pppol2tp.h"
+#include "netif/ppp/pppos.h"
+
+#if LWIP_MPU_COMPATIBLE
+LWIP_MEMPOOL_DECLARE(PPPAPI_MSG, MEMP_NUM_PPP_API_MSG, sizeof(struct pppapi_msg), "PPPAPI_MSG")
+#endif
+
+#define PPPAPI_VAR_REF(name) API_VAR_REF(name)
+#define PPPAPI_VAR_DECLARE(name) API_VAR_DECLARE(struct pppapi_msg, name)
+#define PPPAPI_VAR_ALLOC(name) API_VAR_ALLOC_POOL(struct pppapi_msg, PPPAPI_MSG, name, ERR_MEM)
+#define PPPAPI_VAR_ALLOC_RETURN_NULL(name) API_VAR_ALLOC_POOL(struct pppapi_msg, PPPAPI_MSG, name, NULL)
+#define PPPAPI_VAR_FREE(name) API_VAR_FREE_POOL(PPPAPI_MSG, name)
+
+/**
+ * Call ppp_set_default() inside the tcpip_thread context.
+ */
+static err_t
+pppapi_do_ppp_set_default(struct tcpip_api_call_data *m)
+{
+ /* cast through void* to silence alignment warnings.
+ * We know it works because the structs have been instantiated as struct pppapi_msg */
+ struct pppapi_msg *msg = (struct pppapi_msg *)(void*)m;
+
+ ppp_set_default(msg->msg.ppp);
+ return ERR_OK;
+}
+
+/**
+ * Call ppp_set_default() in a thread-safe way by running that function inside the
+ * tcpip_thread context.
+ */
+err_t
+pppapi_set_default(ppp_pcb *pcb)
+{
+ err_t err;
+ PPPAPI_VAR_DECLARE(msg);
+ PPPAPI_VAR_ALLOC(msg);
+
+ PPPAPI_VAR_REF(msg).msg.ppp = pcb;
+ err = tcpip_api_call(pppapi_do_ppp_set_default, &PPPAPI_VAR_REF(msg).call);
+ PPPAPI_VAR_FREE(msg);
+ return err;
+}
+
+
+#if PPP_NOTIFY_PHASE
+/**
+ * Call ppp_set_notify_phase_callback() inside the tcpip_thread context.
+ */
+static err_t
+pppapi_do_ppp_set_notify_phase_callback(struct tcpip_api_call_data *m)
+{
+ /* cast through void* to silence alignment warnings.
+ * We know it works because the structs have been instantiated as struct pppapi_msg */
+ struct pppapi_msg *msg = (struct pppapi_msg *)(void*)m;
+
+ ppp_set_notify_phase_callback(msg->msg.ppp, msg->msg.msg.setnotifyphasecb.notify_phase_cb);
+ return ERR_OK;
+}
+
+/**
+ * Call ppp_set_notify_phase_callback() in a thread-safe way by running that function inside the
+ * tcpip_thread context.
+ */
+err_t
+pppapi_set_notify_phase_callback(ppp_pcb *pcb, ppp_notify_phase_cb_fn notify_phase_cb)
+{
+ err_t err;
+ PPPAPI_VAR_DECLARE(msg);
+ PPPAPI_VAR_ALLOC(msg);
+
+ PPPAPI_VAR_REF(msg).msg.ppp = pcb;
+ PPPAPI_VAR_REF(msg).msg.msg.setnotifyphasecb.notify_phase_cb = notify_phase_cb;
+ err = tcpip_api_call(pppapi_do_ppp_set_notify_phase_callback, &PPPAPI_VAR_REF(msg).call);
+ PPPAPI_VAR_FREE(msg);
+ return err;
+}
+#endif /* PPP_NOTIFY_PHASE */
+
+
+#if PPPOS_SUPPORT
+/**
+ * Call pppos_create() inside the tcpip_thread context.
+ */
+static err_t
+pppapi_do_pppos_create(struct tcpip_api_call_data *m)
+{
+ /* cast through void* to silence alignment warnings.
+ * We know it works because the structs have been instantiated as struct pppapi_msg */
+ struct pppapi_msg *msg = (struct pppapi_msg *)(void*)m;
+
+ msg->msg.ppp = pppos_create(msg->msg.msg.serialcreate.pppif, msg->msg.msg.serialcreate.output_cb,
+ msg->msg.msg.serialcreate.link_status_cb, msg->msg.msg.serialcreate.ctx_cb);
+ return ERR_OK;
+}
+
+/**
+ * Call pppos_create() in a thread-safe way by running that function inside the
+ * tcpip_thread context.
+ */
+ppp_pcb*
+pppapi_pppos_create(struct netif *pppif, pppos_output_cb_fn output_cb,
+ ppp_link_status_cb_fn link_status_cb, void *ctx_cb)
+{
+ ppp_pcb* result;
+ PPPAPI_VAR_DECLARE(msg);
+ PPPAPI_VAR_ALLOC_RETURN_NULL(msg);
+
+ PPPAPI_VAR_REF(msg).msg.ppp = NULL;
+ PPPAPI_VAR_REF(msg).msg.msg.serialcreate.pppif = pppif;
+ PPPAPI_VAR_REF(msg).msg.msg.serialcreate.output_cb = output_cb;
+ PPPAPI_VAR_REF(msg).msg.msg.serialcreate.link_status_cb = link_status_cb;
+ PPPAPI_VAR_REF(msg).msg.msg.serialcreate.ctx_cb = ctx_cb;
+ tcpip_api_call(pppapi_do_pppos_create, &PPPAPI_VAR_REF(msg).call);
+ result = PPPAPI_VAR_REF(msg).msg.ppp;
+ PPPAPI_VAR_FREE(msg);
+ return result;
+}
+#endif /* PPPOS_SUPPORT */
+
+
+#if PPPOE_SUPPORT
+/**
+ * Call pppoe_create() inside the tcpip_thread context.
+ */
+static err_t
+pppapi_do_pppoe_create(struct tcpip_api_call_data *m)
+{
+ /* cast through void* to silence alignment warnings.
+ * We know it works because the structs have been instantiated as struct pppapi_msg */
+ struct pppapi_msg *msg = (struct pppapi_msg *)(void*)m;
+
+ msg->msg.ppp = pppoe_create(msg->msg.msg.ethernetcreate.pppif, msg->msg.msg.ethernetcreate.ethif,
+ msg->msg.msg.ethernetcreate.service_name, msg->msg.msg.ethernetcreate.concentrator_name,
+ msg->msg.msg.ethernetcreate.link_status_cb, msg->msg.msg.ethernetcreate.ctx_cb);
+ return ERR_OK;
+}
+
+/**
+ * Call pppoe_create() in a thread-safe way by running that function inside the
+ * tcpip_thread context.
+ */
+ppp_pcb*
+pppapi_pppoe_create(struct netif *pppif, struct netif *ethif, const char *service_name,
+ const char *concentrator_name, ppp_link_status_cb_fn link_status_cb,
+ void *ctx_cb)
+{
+ ppp_pcb* result;
+ PPPAPI_VAR_DECLARE(msg);
+ PPPAPI_VAR_ALLOC_RETURN_NULL(msg);
+
+ PPPAPI_VAR_REF(msg).msg.ppp = NULL;
+ PPPAPI_VAR_REF(msg).msg.msg.ethernetcreate.pppif = pppif;
+ PPPAPI_VAR_REF(msg).msg.msg.ethernetcreate.ethif = ethif;
+ PPPAPI_VAR_REF(msg).msg.msg.ethernetcreate.service_name = service_name;
+ PPPAPI_VAR_REF(msg).msg.msg.ethernetcreate.concentrator_name = concentrator_name;
+ PPPAPI_VAR_REF(msg).msg.msg.ethernetcreate.link_status_cb = link_status_cb;
+ PPPAPI_VAR_REF(msg).msg.msg.ethernetcreate.ctx_cb = ctx_cb;
+ tcpip_api_call(pppapi_do_pppoe_create, &PPPAPI_VAR_REF(msg).call);
+ result = PPPAPI_VAR_REF(msg).msg.ppp;
+ PPPAPI_VAR_FREE(msg);
+ return result;
+}
+#endif /* PPPOE_SUPPORT */
+
+
+#if PPPOL2TP_SUPPORT
+/**
+ * Call pppol2tp_create() inside the tcpip_thread context.
+ */
+static err_t
+pppapi_do_pppol2tp_create(struct tcpip_api_call_data *m)
+{
+ /* cast through void* to silence alignment warnings.
+ * We know it works because the structs have been instantiated as struct pppapi_msg */
+ struct pppapi_msg *msg = (struct pppapi_msg *)(void*)m;
+
+ msg->msg.ppp = pppol2tp_create(msg->msg.msg.l2tpcreate.pppif,
+ msg->msg.msg.l2tpcreate.netif, API_EXPR_REF(msg->msg.msg.l2tpcreate.ipaddr), msg->msg.msg.l2tpcreate.port,
+#if PPPOL2TP_AUTH_SUPPORT
+ msg->msg.msg.l2tpcreate.secret,
+ msg->msg.msg.l2tpcreate.secret_len,
+#else /* PPPOL2TP_AUTH_SUPPORT */
+ NULL,
+ 0,
+#endif /* PPPOL2TP_AUTH_SUPPORT */
+ msg->msg.msg.l2tpcreate.link_status_cb, msg->msg.msg.l2tpcreate.ctx_cb);
+ return ERR_OK;
+}
+
+/**
+ * Call pppol2tp_create() in a thread-safe way by running that function inside the
+ * tcpip_thread context.
+ */
+ppp_pcb*
+pppapi_pppol2tp_create(struct netif *pppif, struct netif *netif, ip_addr_t *ipaddr, u16_t port,
+ const u8_t *secret, u8_t secret_len,
+ ppp_link_status_cb_fn link_status_cb, void *ctx_cb)
+{
+ ppp_pcb* result;
+ PPPAPI_VAR_DECLARE(msg);
+ PPPAPI_VAR_ALLOC_RETURN_NULL(msg);
+#if !PPPOL2TP_AUTH_SUPPORT
+ LWIP_UNUSED_ARG(secret);
+ LWIP_UNUSED_ARG(secret_len);
+#endif /* !PPPOL2TP_AUTH_SUPPORT */
+
+ PPPAPI_VAR_REF(msg).msg.ppp = NULL;
+ PPPAPI_VAR_REF(msg).msg.msg.l2tpcreate.pppif = pppif;
+ PPPAPI_VAR_REF(msg).msg.msg.l2tpcreate.netif = netif;
+ PPPAPI_VAR_REF(msg).msg.msg.l2tpcreate.ipaddr = PPPAPI_VAR_REF(ipaddr);
+ PPPAPI_VAR_REF(msg).msg.msg.l2tpcreate.port = port;
+#if PPPOL2TP_AUTH_SUPPORT
+ PPPAPI_VAR_REF(msg).msg.msg.l2tpcreate.secret = secret;
+ PPPAPI_VAR_REF(msg).msg.msg.l2tpcreate.secret_len = secret_len;
+#endif /* PPPOL2TP_AUTH_SUPPORT */
+ PPPAPI_VAR_REF(msg).msg.msg.l2tpcreate.link_status_cb = link_status_cb;
+ PPPAPI_VAR_REF(msg).msg.msg.l2tpcreate.ctx_cb = ctx_cb;
+ tcpip_api_call(pppapi_do_pppol2tp_create, &PPPAPI_VAR_REF(msg).call);
+ result = PPPAPI_VAR_REF(msg).msg.ppp;
+ PPPAPI_VAR_FREE(msg);
+ return result;
+}
+#endif /* PPPOL2TP_SUPPORT */
+
+
+/**
+ * Call ppp_connect() inside the tcpip_thread context.
+ */
+static err_t
+pppapi_do_ppp_connect(struct tcpip_api_call_data *m)
+{
+ /* cast through void* to silence alignment warnings.
+ * We know it works because the structs have been instantiated as struct pppapi_msg */
+ struct pppapi_msg *msg = (struct pppapi_msg *)(void*)m;
+
+ return ppp_connect(msg->msg.ppp, msg->msg.msg.connect.holdoff);
+}
+
+/**
+ * Call ppp_connect() in a thread-safe way by running that function inside the
+ * tcpip_thread context.
+ */
+err_t
+pppapi_connect(ppp_pcb *pcb, u16_t holdoff)
+{
+ err_t err;
+ PPPAPI_VAR_DECLARE(msg);
+ PPPAPI_VAR_ALLOC(msg);
+
+ PPPAPI_VAR_REF(msg).msg.ppp = pcb;
+ PPPAPI_VAR_REF(msg).msg.msg.connect.holdoff = holdoff;
+ err = tcpip_api_call(pppapi_do_ppp_connect, &PPPAPI_VAR_REF(msg).call);
+ PPPAPI_VAR_FREE(msg);
+ return err;
+}
+
+
+#if PPP_SERVER
+/**
+ * Call ppp_listen() inside the tcpip_thread context.
+ */
+static err_t
+pppapi_do_ppp_listen(struct tcpip_api_call_data *m)
+{
+ /* cast through void* to silence alignment warnings.
+ * We know it works because the structs have been instantiated as struct pppapi_msg */
+ struct pppapi_msg *msg = (struct pppapi_msg *)(void*)m;
+
+ return ppp_listen(msg->msg.ppp);
+}
+
+/**
+ * Call ppp_listen() in a thread-safe way by running that function inside the
+ * tcpip_thread context.
+ */
+err_t
+pppapi_listen(ppp_pcb *pcb)
+{
+ err_t err;
+ PPPAPI_VAR_DECLARE(msg);
+ PPPAPI_VAR_ALLOC(msg);
+
+ PPPAPI_VAR_REF(msg).msg.ppp = pcb;
+ err = tcpip_api_call(pppapi_do_ppp_listen, &PPPAPI_VAR_REF(msg).call);
+ PPPAPI_VAR_FREE(msg);
+ return err;
+}
+#endif /* PPP_SERVER */
+
+
+/**
+ * Call ppp_close() inside the tcpip_thread context.
+ */
+static err_t
+pppapi_do_ppp_close(struct tcpip_api_call_data *m)
+{
+ /* cast through void* to silence alignment warnings.
+ * We know it works because the structs have been instantiated as struct pppapi_msg */
+ struct pppapi_msg *msg = (struct pppapi_msg *)(void*)m;
+
+ return ppp_close(msg->msg.ppp, msg->msg.msg.close.nocarrier);
+}
+
+/**
+ * Call ppp_close() in a thread-safe way by running that function inside the
+ * tcpip_thread context.
+ */
+err_t
+pppapi_close(ppp_pcb *pcb, u8_t nocarrier)
+{
+ err_t err;
+ PPPAPI_VAR_DECLARE(msg);
+ PPPAPI_VAR_ALLOC(msg);
+
+ PPPAPI_VAR_REF(msg).msg.ppp = pcb;
+ PPPAPI_VAR_REF(msg).msg.msg.close.nocarrier = nocarrier;
+ err = tcpip_api_call(pppapi_do_ppp_close, &PPPAPI_VAR_REF(msg).call);
+ PPPAPI_VAR_FREE(msg);
+ return err;
+}
+
+
+/**
+ * Call ppp_free() inside the tcpip_thread context.
+ */
+static err_t
+pppapi_do_ppp_free(struct tcpip_api_call_data *m)
+{
+ /* cast through void* to silence alignment warnings.
+ * We know it works because the structs have been instantiated as struct pppapi_msg */
+ struct pppapi_msg *msg = (struct pppapi_msg *)(void*)m;
+
+ return ppp_free(msg->msg.ppp);
+}
+
+/**
+ * Call ppp_free() in a thread-safe way by running that function inside the
+ * tcpip_thread context.
+ */
+err_t
+pppapi_free(ppp_pcb *pcb)
+{
+ err_t err;
+ PPPAPI_VAR_DECLARE(msg);
+ PPPAPI_VAR_ALLOC(msg);
+
+ PPPAPI_VAR_REF(msg).msg.ppp = pcb;
+ err = tcpip_api_call(pppapi_do_ppp_free, &PPPAPI_VAR_REF(msg).call);
+ PPPAPI_VAR_FREE(msg);
+ return err;
+}
+
+
+/**
+ * Call ppp_ioctl() inside the tcpip_thread context.
+ */
+static err_t
+pppapi_do_ppp_ioctl(struct tcpip_api_call_data *m)
+{
+ /* cast through void* to silence alignment warnings.
+ * We know it works because the structs have been instantiated as struct pppapi_msg */
+ struct pppapi_msg *msg = (struct pppapi_msg *)(void*)m;
+
+ return ppp_ioctl(msg->msg.ppp, msg->msg.msg.ioctl.cmd, msg->msg.msg.ioctl.arg);
+}
+
+/**
+ * Call ppp_ioctl() in a thread-safe way by running that function inside the
+ * tcpip_thread context.
+ */
+err_t
+pppapi_ioctl(ppp_pcb *pcb, u8_t cmd, void *arg)
+{
+ err_t err;
+ PPPAPI_VAR_DECLARE(msg);
+ PPPAPI_VAR_ALLOC(msg);
+
+ PPPAPI_VAR_REF(msg).msg.ppp = pcb;
+ PPPAPI_VAR_REF(msg).msg.msg.ioctl.cmd = cmd;
+ PPPAPI_VAR_REF(msg).msg.msg.ioctl.arg = arg;
+ err = tcpip_api_call(pppapi_do_ppp_ioctl, &PPPAPI_VAR_REF(msg).call);
+ PPPAPI_VAR_FREE(msg);
+ return err;
+}
+
+#endif /* LWIP_PPP_API */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/pppcrypt.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/pppcrypt.c
new file mode 100644
index 0000000..82d78c1
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/pppcrypt.c
@@ -0,0 +1,66 @@
+/*
+ * pppcrypt.c - PPP/DES linkage for MS-CHAP and EAP SRP-SHA1
+ *
+ * Extracted from chap_ms.c by James Carlson.
+ *
+ * Copyright (c) 1995 Eric Rosenquist. 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(s) of the authors of this software must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission.
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT && MSCHAP_SUPPORT /* don't build if not necessary */
+
+#include "netif/ppp/ppp_impl.h"
+
+#include "netif/ppp/pppcrypt.h"
+
+
+static u_char pppcrypt_get_7bits(u_char *input, int startBit) {
+ unsigned int word;
+
+ word = (unsigned)input[startBit / 8] << 8;
+ word |= (unsigned)input[startBit / 8 + 1];
+
+ word >>= 15 - (startBit % 8 + 7);
+
+ return word & 0xFE;
+}
+
+/* IN 56 bit DES key missing parity bits
+ * OUT 64 bit DES key with parity bits added
+ */
+void pppcrypt_56_to_64_bit_key(u_char *key, u_char * des_key) {
+ des_key[0] = pppcrypt_get_7bits(key, 0);
+ des_key[1] = pppcrypt_get_7bits(key, 7);
+ des_key[2] = pppcrypt_get_7bits(key, 14);
+ des_key[3] = pppcrypt_get_7bits(key, 21);
+ des_key[4] = pppcrypt_get_7bits(key, 28);
+ des_key[5] = pppcrypt_get_7bits(key, 35);
+ des_key[6] = pppcrypt_get_7bits(key, 42);
+ des_key[7] = pppcrypt_get_7bits(key, 49);
+}
+
+#endif /* PPP_SUPPORT && MSCHAP_SUPPORT */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/pppoe.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/pppoe.c
new file mode 100644
index 0000000..eabfa4d
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/pppoe.c
@@ -0,0 +1,1191 @@
+/*****************************************************************************
+* pppoe.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 "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT && PPPOE_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#if 0 /* UNUSED */
+#include <string.h>
+#include <stdio.h>
+#endif /* UNUSED */
+
+#include "lwip/timeouts.h"
+#include "lwip/memp.h"
+#include "lwip/stats.h"
+#include "lwip/snmp.h"
+
+#include "netif/ethernet.h"
+#include "netif/ppp/ppp_impl.h"
+#include "netif/ppp/lcp.h"
+#include "netif/ppp/ipcp.h"
+#include "netif/ppp/pppoe.h"
+
+/* Memory pool */
+LWIP_MEMPOOL_DECLARE(PPPOE_IF, MEMP_NUM_PPPOE_INTERFACES, sizeof(struct pppoe_softc), "PPPOE_IF")
+
+/* 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
+
+#define PPPOE_ERRORSTRING_LEN 64
+
+
+/* callbacks called from PPP core */
+static err_t pppoe_write(ppp_pcb *ppp, void *ctx, struct pbuf *p);
+static err_t pppoe_netif_output(ppp_pcb *ppp, void *ctx, struct pbuf *p, u_short protocol);
+static void pppoe_connect(ppp_pcb *ppp, void *ctx);
+static void pppoe_disconnect(ppp_pcb *ppp, void *ctx);
+static err_t pppoe_destroy(ppp_pcb *ppp, void *ctx);
+
+/* management routines */
+static void pppoe_abort_connect(struct pppoe_softc *);
+#if 0 /* UNUSED */
+static void pppoe_clear_softc(struct pppoe_softc *, const char *);
+#endif /* UNUSED */
+
+/* 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 err_t pppoe_xmit(struct pppoe_softc *sc, struct pbuf *pb);
+static struct pppoe_softc* pppoe_find_softc_by_session(u_int session, struct netif *rcvif);
+static struct pppoe_softc* pppoe_find_softc_by_hunique(u8_t *token, size_t len, struct netif *rcvif);
+
+/** linked list of created pppoe interfaces */
+static struct pppoe_softc *pppoe_softc_list;
+
+/* Callbacks structure for PPP core */
+static const struct link_callbacks pppoe_callbacks = {
+ pppoe_connect,
+#if PPP_SERVER
+ NULL,
+#endif /* PPP_SERVER */
+ pppoe_disconnect,
+ pppoe_destroy,
+ pppoe_write,
+ pppoe_netif_output,
+ NULL,
+ NULL
+};
+
+/*
+ * Create a new PPP Over Ethernet (PPPoE) connection.
+ *
+ * Return 0 on success, an error code on failure.
+ */
+ppp_pcb *pppoe_create(struct netif *pppif,
+ struct netif *ethif,
+ const char *service_name, const char *concentrator_name,
+ ppp_link_status_cb_fn link_status_cb, void *ctx_cb)
+{
+ ppp_pcb *ppp;
+ struct pppoe_softc *sc;
+ LWIP_UNUSED_ARG(service_name);
+ LWIP_UNUSED_ARG(concentrator_name);
+
+ sc = (struct pppoe_softc *)LWIP_MEMPOOL_ALLOC(PPPOE_IF);
+ if (sc == NULL) {
+ return NULL;
+ }
+
+ ppp = ppp_new(pppif, &pppoe_callbacks, sc, link_status_cb, ctx_cb);
+ if (ppp == NULL) {
+ LWIP_MEMPOOL_FREE(PPPOE_IF, sc);
+ return NULL;
+ }
+
+ memset(sc, 0, sizeof(struct pppoe_softc));
+ sc->pcb = ppp;
+ sc->sc_ethif = ethif;
+ /* put the new interface at the head of the list */
+ sc->next = pppoe_softc_list;
+ pppoe_softc_list = sc;
+ return ppp;
+}
+
+/* Called by PPP core */
+static err_t pppoe_write(ppp_pcb *ppp, void *ctx, struct pbuf *p) {
+ struct pppoe_softc *sc = (struct pppoe_softc *)ctx;
+ struct pbuf *ph; /* Ethernet + PPPoE header */
+ err_t ret;
+#if MIB2_STATS
+ u16_t tot_len;
+#else /* MIB2_STATS */
+ LWIP_UNUSED_ARG(ppp);
+#endif /* MIB2_STATS */
+
+ /* skip address & flags */
+ pbuf_header(p, -(s16_t)2);
+
+ ph = pbuf_alloc(PBUF_LINK, (u16_t)(PPPOE_HEADERLEN), PBUF_RAM);
+ if(!ph) {
+ LINK_STATS_INC(link.memerr);
+ LINK_STATS_INC(link.proterr);
+ MIB2_STATS_NETIF_INC(ppp->netif, ifoutdiscards);
+ pbuf_free(p);
+ return ERR_MEM;
+ }
+
+ pbuf_header(ph, -(s16_t)PPPOE_HEADERLEN); /* hide PPPoE header */
+ pbuf_cat(ph, p);
+#if MIB2_STATS
+ tot_len = ph->tot_len;
+#endif /* MIB2_STATS */
+
+ ret = pppoe_xmit(sc, ph);
+ if (ret != ERR_OK) {
+ LINK_STATS_INC(link.err);
+ MIB2_STATS_NETIF_INC(ppp->netif, ifoutdiscards);
+ return ret;
+ }
+
+ MIB2_STATS_NETIF_ADD(ppp->netif, ifoutoctets, (u16_t)tot_len);
+ MIB2_STATS_NETIF_INC(ppp->netif, ifoutucastpkts);
+ LINK_STATS_INC(link.xmit);
+ return ERR_OK;
+}
+
+/* Called by PPP core */
+static err_t pppoe_netif_output(ppp_pcb *ppp, void *ctx, struct pbuf *p, u_short protocol) {
+ struct pppoe_softc *sc = (struct pppoe_softc *)ctx;
+ struct pbuf *pb;
+ u8_t *pl;
+ err_t err;
+#if MIB2_STATS
+ u16_t tot_len;
+#else /* MIB2_STATS */
+ LWIP_UNUSED_ARG(ppp);
+#endif /* MIB2_STATS */
+
+ /* @todo: try to use pbuf_header() here! */
+ pb = pbuf_alloc(PBUF_LINK, PPPOE_HEADERLEN + sizeof(protocol), PBUF_RAM);
+ if(!pb) {
+ LINK_STATS_INC(link.memerr);
+ LINK_STATS_INC(link.proterr);
+ MIB2_STATS_NETIF_INC(ppp->netif, ifoutdiscards);
+ return ERR_MEM;
+ }
+
+ pbuf_header(pb, -(s16_t)PPPOE_HEADERLEN);
+
+ pl = (u8_t*)pb->payload;
+ PUTSHORT(protocol, pl);
+
+ pbuf_chain(pb, p);
+#if MIB2_STATS
+ tot_len = pb->tot_len;
+#endif /* MIB2_STATS */
+
+ if( (err = pppoe_xmit(sc, pb)) != ERR_OK) {
+ LINK_STATS_INC(link.err);
+ MIB2_STATS_NETIF_INC(ppp->netif, ifoutdiscards);
+ return err;
+ }
+
+ MIB2_STATS_NETIF_ADD(ppp->netif, ifoutoctets, tot_len);
+ MIB2_STATS_NETIF_INC(ppp->netif, ifoutucastpkts);
+ LINK_STATS_INC(link.xmit);
+ return ERR_OK;
+}
+
+static err_t
+pppoe_destroy(ppp_pcb *ppp, void *ctx)
+{
+ struct pppoe_softc *sc = (struct pppoe_softc *)ctx;
+ struct pppoe_softc **copp, *freep;
+ LWIP_UNUSED_ARG(ppp);
+
+ sys_untimeout(pppoe_timeout, sc);
+
+ /* remove interface from list */
+ for (copp = &pppoe_softc_list; (freep = *copp); copp = &freep->next) {
+ if (freep == sc) {
+ *copp = freep->next;
+ break;
+ }
+ }
+
+#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 */
+ LWIP_MEMPOOL_FREE(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;
+
+ for (sc = pppoe_softc_list; sc != NULL; sc = sc->next) {
+ if (sc->sc_state == PPPOE_STATE_SESSION
+ && sc->sc_session == session
+ && sc->sc_ethif == rcvif) {
+ return sc;
+ }
+ }
+ 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 (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) {
+ PPPDEBUG(LOG_DEBUG, ("%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) {
+ PPPDEBUG(LOG_DEBUG, ("%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;
+}
+
+/* analyze and handle a single received packet while not in session state */
+void
+pppoe_disc_input(struct netif *netif, struct pbuf *pb)
+{
+ u16_t tag, len;
+ u16_t session, plen;
+ struct pppoe_softc *sc;
+#if PPP_DEBUG
+ const char *err_msg = NULL;
+#endif /* PPP_DEBUG */
+ 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;
+ struct eth_hdr *ethhdr;
+
+ /* don't do anything if there is not a single PPPoE instance */
+ if (pppoe_softc_list == NULL) {
+ pbuf_free(pb);
+ return;
+ }
+
+ pb = ppp_singlebuf(pb);
+
+ 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 < (u16_t)PPPOE_HEADERLEN) {
+ PPPDEBUG(LOG_DEBUG, ("pppoe: packet too short: %d\n", pb->len));
+ goto done;
+ }
+
+ ph = (struct pppoehdr *) (ethhdr + 1);
+ if (ph->vertype != PPPOE_VERTYPE) {
+ PPPDEBUG(LOG_DEBUG, ("pppoe: unknown version/type packet: 0x%x\n", ph->vertype));
+ goto done;
+ }
+ session = lwip_ntohs(ph->session);
+ plen = lwip_ntohs(ph->plen);
+ off += sizeof(*ph);
+
+ if (plen + off > pb->len) {
+ PPPDEBUG(LOG_DEBUG, ("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 = lwip_ntohs(pt.tag);
+ len = lwip_ntohs(pt.len);
+ if (off + sizeof(pt) + len > pb->len) {
+ PPPDEBUG(LOG_DEBUG, ("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);
+ break;
+ case PPPOE_TAG_ACCOOKIE:
+ if (ac_cookie == NULL) {
+ if (len > PPPOE_MAX_AC_COOKIE_LEN) {
+ PPPDEBUG(LOG_DEBUG, ("pppoe: AC cookie is too long: len = %d, max = %d\n", len, PPPOE_MAX_AC_COOKIE_LEN));
+ goto done;
+ }
+ ac_cookie = (u8_t*)pb->payload + off + sizeof(pt);
+ ac_cookie_len = len;
+ }
+ break;
+#if PPP_DEBUG
+ case PPPOE_TAG_SNAME_ERR:
+ err_msg = "SERVICE NAME ERROR";
+ break;
+ case PPPOE_TAG_ACSYS_ERR:
+ err_msg = "AC SYSTEM ERROR";
+ break;
+ case PPPOE_TAG_GENERIC_ERR:
+ err_msg = "GENERIC ERROR";
+ break;
+#endif /* PPP_DEBUG */
+ default:
+ break;
+ }
+#if PPP_DEBUG
+ if (err_msg != NULL) {
+ char error_tmp[PPPOE_ERRORSTRING_LEN];
+ u16_t error_len = LWIP_MIN(len, sizeof(error_tmp)-1);
+ strncpy(error_tmp, (char*)pb->payload + off + sizeof(pt), error_len);
+ error_tmp[error_len] = '\0';
+ if (sc) {
+ PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": %s: %s\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, err_msg, error_tmp));
+ } else {
+ PPPDEBUG(LOG_DEBUG, ("pppoe: %s: %s\n", err_msg, error_tmp));
+ }
+ }
+#endif /* PPP_DEBUG */
+ 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) {
+ /* PPPDEBUG(LOG_DEBUG, ("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 */
+ PPPDEBUG(LOG_DEBUG, ("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)) {
+ PPPDEBUG(LOG_DEBUG, ("pppoe: received PADR but could not find request for it\n"));
+ }
+ goto done;
+ }
+ if (sc->sc_state != PPPOE_STATE_PADO_SENT) {
+ PPPDEBUG(LOG_DEBUG, ("%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;
+ ppp_start(sc->pcb); /* 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) {
+ PPPDEBUG(LOG_DEBUG, ("pppoe: received PADO but could not find request for it\n"));
+ }
+ goto done;
+ }
+ if (sc->sc_state != PPPOE_STATE_PADI_SENT) {
+ PPPDEBUG(LOG_DEBUG, ("%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;
+ ppp_start(sc->pcb); /* notify upper layers */
+ break;
+ case PPPOE_CODE_PADT:
+ /* Don't disconnect here, we let the LCP Echo/Reply find the fact
+ * that PPP session is down. Asking the PPP stack to end the session
+ * require strict checking about the PPP phase to prevent endless
+ * disconnection loops.
+ */
+#if 0 /* UNUSED */
+ if (sc == NULL) { /* PADT frames are rarely sent with a hunique tag, this is actually almost always true */
+ goto done;
+ }
+ pppoe_clear_softc(sc, "received PADT");
+#endif /* UNUSED */
+ break;
+ default:
+ if(sc) {
+ PPPDEBUG(LOG_DEBUG, ("%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 {
+ PPPDEBUG(LOG_DEBUG, ("pppoe: unknown code (0x%"X16_F") session = 0x%"X16_F"\n", (u16_t)ph->code, session));
+ }
+ break;
+ }
+
+done:
+ pbuf_free(pb);
+ return;
+}
+
+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, -(s16_t)sizeof(struct eth_hdr)) != 0) {
+ /* bail out */
+ PPPDEBUG(LOG_ERR, ("pppoe_data_input: pbuf_header failed\n"));
+ LINK_STATS_INC(link.lenerr);
+ goto drop;
+ }
+
+ if (pb->len < sizeof(*ph)) {
+ PPPDEBUG(LOG_DEBUG, ("pppoe_data_input: could not get PPPoE header\n"));
+ goto drop;
+ }
+ ph = (struct pppoehdr *)pb->payload;
+
+ if (ph->vertype != PPPOE_VERTYPE) {
+ PPPDEBUG(LOG_DEBUG, ("pppoe (data): unknown version/type packet: 0x%x\n", ph->vertype));
+ goto drop;
+ }
+ if (ph->code != 0) {
+ goto drop;
+ }
+
+ session = lwip_ntohs(ph->session);
+ sc = pppoe_find_softc_by_session(session, netif);
+ if (sc == NULL) {
+#ifdef PPPOE_TERM_UNKNOWN_SESSIONS
+ PPPDEBUG(LOG_DEBUG, ("pppoe: input for unknown session 0x%x, sending PADT\n", session));
+ pppoe_send_padt(netif, session, shost);
+#endif
+ goto drop;
+ }
+
+ plen = lwip_ntohs(ph->plen);
+
+ if (pbuf_header(pb, -(s16_t)(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->tot_len < plen) {
+ goto drop;
+ }
+
+ /* Dispatch the packet thereby consuming it. */
+ ppp_input(sc->pcb, 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;
+
+ /* make room for Ethernet header - should not fail */
+ if (pbuf_header(pb, (s16_t)(sizeof(struct eth_hdr))) != 0) {
+ /* bail out */
+ PPPDEBUG(LOG_ERR, ("pppoe: %c%c%"U16_F": pppoe_output: could not allocate room for Ethernet 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;
+ }
+ ethhdr = (struct eth_hdr *)pb->payload;
+ etype = sc->sc_state == PPPOE_STATE_SESSION ? ETHTYPE_PPPOE : ETHTYPE_PPPOEDISC;
+ ethhdr->type = lwip_htons(etype);
+ MEMCPY(&ethhdr->dest.addr, &sc->sc_dest.addr, sizeof(ethhdr->dest.addr));
+ MEMCPY(&ethhdr->src.addr, &sc->sc_ethif->hwaddr, 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 */
+
+ /* 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)(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;
+ /* 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)
+{
+ u32_t retry_wait;
+ int 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.
+ */
+ if (sc->sc_padi_retried < 0xff) {
+ sc->sc_padi_retried++;
+ }
+ if (!sc->pcb->settings.persist && 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;
+ }
+ }
+ /* initialize for quick retry mode */
+ retry_wait = LWIP_MIN(PPPOE_DISC_TIMEOUT * sc->sc_padi_retried, PPPOE_SLOW_RETRY);
+ 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;
+ default:
+ return; /* all done, work in peace */
+ }
+}
+
+/* Start a connection (i.e. initiate discovery phase) */
+static void
+pppoe_connect(ppp_pcb *ppp, void *ctx)
+{
+ err_t err;
+ struct pppoe_softc *sc = (struct pppoe_softc *)ctx;
+ lcp_options *lcp_wo;
+ lcp_options *lcp_ao;
+#if PPP_IPV4_SUPPORT && VJ_SUPPORT
+ ipcp_options *ipcp_wo;
+ ipcp_options *ipcp_ao;
+#endif /* PPP_IPV4_SUPPORT && VJ_SUPPORT */
+
+ sc->sc_session = 0;
+ sc->sc_ac_cookie_len = 0;
+ sc->sc_padi_retried = 0;
+ sc->sc_padr_retried = 0;
+ /* changed to real address later */
+ MEMCPY(&sc->sc_dest, ethbroadcast.addr, sizeof(sc->sc_dest));
+#ifdef PPPOE_SERVER
+ /* wait PADI if IFF_PASSIVE */
+ if ((sc->sc_sppp.pp_if.if_flags & IFF_PASSIVE)) {
+ return 0;
+ }
+#endif
+
+ lcp_wo = &ppp->lcp_wantoptions;
+ lcp_wo->mru = sc->sc_ethif->mtu-PPPOE_HEADERLEN-2; /* two byte PPP protocol discriminator, then IP data */
+ lcp_wo->neg_asyncmap = 0;
+ lcp_wo->neg_pcompression = 0;
+ lcp_wo->neg_accompression = 0;
+ lcp_wo->passive = 0;
+ lcp_wo->silent = 0;
+
+ lcp_ao = &ppp->lcp_allowoptions;
+ lcp_ao->mru = sc->sc_ethif->mtu-PPPOE_HEADERLEN-2; /* two byte PPP protocol discriminator, then IP data */
+ lcp_ao->neg_asyncmap = 0;
+ lcp_ao->neg_pcompression = 0;
+ lcp_ao->neg_accompression = 0;
+
+#if PPP_IPV4_SUPPORT && VJ_SUPPORT
+ ipcp_wo = &ppp->ipcp_wantoptions;
+ ipcp_wo->neg_vj = 0;
+ ipcp_wo->old_vj = 0;
+
+ ipcp_ao = &ppp->ipcp_allowoptions;
+ ipcp_ao->neg_vj = 0;
+ ipcp_ao->old_vj = 0;
+#endif /* PPP_IPV4_SUPPORT && VJ_SUPPORT */
+
+ /* save state, in case we fail to send PADI */
+ sc->sc_state = PPPOE_STATE_PADI_SENT;
+ 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, pppoe_timeout, sc);
+}
+
+/* disconnect */
+static void
+pppoe_disconnect(ppp_pcb *ppp, void *ctx)
+{
+ struct pppoe_softc *sc = (struct pppoe_softc *)ctx;
+
+ PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": disconnecting\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num));
+ if (sc->sc_state == PPPOE_STATE_SESSION) {
+ pppoe_send_padt(sc->sc_ethif, sc->sc_session, (const u8_t *)&sc->sc_dest);
+ }
+
+ /* stop any timer, disconnect can be called while initiating is in progress */
+ sys_untimeout(pppoe_timeout, sc);
+ sc->sc_state = PPPOE_STATE_INITIAL;
+#ifdef PPPOE_SERVER
+ if (sc->sc_hunique) {
+ mem_free(sc->sc_hunique);
+ sc->sc_hunique = NULL; /* probably not necessary, if state is initial we shouldn't have to access hunique anyway */
+ }
+ sc->sc_hunique_len = 0; /* probably not necessary, if state is initial we shouldn't have to access hunique anyway */
+#endif
+ ppp_link_end(ppp); /* notify upper layers */
+ return;
+}
+
+/* Connection attempt aborted */
+static void
+pppoe_abort_connect(struct pppoe_softc *sc)
+{
+ PPPDEBUG(LOG_DEBUG, ("%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_INITIAL;
+ ppp_link_failed(sc->pcb); /* notify upper layers */
+}
+
+/* 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 */
+
+ 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)(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;
+ 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, (u16_t)(PPPOE_HEADERLEN), PBUF_RAM);
+ if (!pb) {
+ return ERR_MEM;
+ }
+ LWIP_ASSERT("pb->tot_len == pb->len", pb->tot_len == pb->len);
+
+ pbuf_header(pb, (s16_t)sizeof(struct eth_hdr));
+ 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, &outgoing_if->hwaddr, 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;
+
+ /* 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, (u16_t)(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;
+ 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 */
+
+ 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, (u16_t)(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;
+ 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
+
+static err_t
+pppoe_xmit(struct pppoe_softc *sc, struct pbuf *pb)
+{
+ u8_t *p;
+ size_t len;
+
+ len = pb->tot_len;
+
+ /* make room for PPPoE header - should not fail */
+ if (pbuf_header(pb, (s16_t)(PPPOE_HEADERLEN)) != 0) {
+ /* bail out */
+ PPPDEBUG(LOG_ERR, ("pppoe: %c%c%"U16_F": pppoe_xmit: could not allocate room for PPPoE 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;
+ 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);
+ PPPDEBUG(LOG_DEBUG, ("%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
+
+#if 0 /* UNUSED */
+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));
+ sc->sc_state = PPPOE_STATE_INITIAL;
+ ppp_link_end(sc->pcb); /* notify upper layers - /!\ dangerous /!\ - see pppoe_disc_input() */
+}
+#endif /* UNUSED */
+#endif /* PPP_SUPPORT && PPPOE_SUPPORT */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/pppol2tp.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/pppol2tp.c
new file mode 100644
index 0000000..d44471e
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/pppol2tp.c
@@ -0,0 +1,1131 @@
+/**
+ * @file
+ * Network Point to Point Protocol over Layer 2 Tunneling Protocol program file.
+ *
+ */
+
+/*
+ * 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.
+ *
+ */
+
+/*
+ * L2TP Support status:
+ *
+ * Supported:
+ * - L2TPv2 (PPP over L2TP, a.k.a. UDP tunnels)
+ * - LAC
+ *
+ * Not supported:
+ * - LNS (require PPP server support)
+ * - L2TPv3 ethernet pseudowires
+ * - L2TPv3 VLAN pseudowire
+ * - L2TPv3 PPP pseudowires
+ * - L2TPv3 IP encapsulation
+ * - L2TPv3 IP pseudowire
+ * - L2TP tunnel switching - http://tools.ietf.org/html/draft-ietf-l2tpext-tunnel-switching-08
+ * - Multiple tunnels per UDP socket, as well as multiple sessions per tunnel
+ * - Hidden AVPs
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT && PPPOL2TP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/err.h"
+#include "lwip/memp.h"
+#include "lwip/netif.h"
+#include "lwip/udp.h"
+#include "lwip/snmp.h"
+
+#include "netif/ppp/ppp_impl.h"
+#include "netif/ppp/lcp.h"
+#include "netif/ppp/ipcp.h"
+#include "netif/ppp/pppol2tp.h"
+#include "netif/ppp/pppcrypt.h"
+#include "netif/ppp/magic.h"
+
+/* Memory pool */
+LWIP_MEMPOOL_DECLARE(PPPOL2TP_PCB, MEMP_NUM_PPPOL2TP_INTERFACES, sizeof(pppol2tp_pcb), "PPPOL2TP_PCB")
+
+/* callbacks called from PPP core */
+static err_t pppol2tp_write(ppp_pcb *ppp, void *ctx, struct pbuf *p);
+static err_t pppol2tp_netif_output(ppp_pcb *ppp, void *ctx, struct pbuf *p, u_short protocol);
+static err_t pppol2tp_destroy(ppp_pcb *ppp, void *ctx); /* Destroy a L2TP control block */
+static void pppol2tp_connect(ppp_pcb *ppp, void *ctx); /* Be a LAC, connect to a LNS. */
+static void pppol2tp_disconnect(ppp_pcb *ppp, void *ctx); /* Disconnect */
+
+ /* Prototypes for procedures local to this file. */
+static void pppol2tp_input(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port);
+static void pppol2tp_dispatch_control_packet(pppol2tp_pcb *l2tp, u16_t port, struct pbuf *p, u16_t ns, u16_t nr);
+static void pppol2tp_timeout(void *arg);
+static void pppol2tp_abort_connect(pppol2tp_pcb *l2tp);
+static err_t pppol2tp_send_sccrq(pppol2tp_pcb *l2tp);
+static err_t pppol2tp_send_scccn(pppol2tp_pcb *l2tp, u16_t ns);
+static err_t pppol2tp_send_icrq(pppol2tp_pcb *l2tp, u16_t ns);
+static err_t pppol2tp_send_iccn(pppol2tp_pcb *l2tp, u16_t ns);
+static err_t pppol2tp_send_zlb(pppol2tp_pcb *l2tp, u16_t ns);
+static err_t pppol2tp_send_stopccn(pppol2tp_pcb *l2tp, u16_t ns);
+static err_t pppol2tp_xmit(pppol2tp_pcb *l2tp, struct pbuf *pb);
+static err_t pppol2tp_udp_send(pppol2tp_pcb *l2tp, struct pbuf *pb);
+
+/* Callbacks structure for PPP core */
+static const struct link_callbacks pppol2tp_callbacks = {
+ pppol2tp_connect,
+#if PPP_SERVER
+ NULL,
+#endif /* PPP_SERVER */
+ pppol2tp_disconnect,
+ pppol2tp_destroy,
+ pppol2tp_write,
+ pppol2tp_netif_output,
+ NULL,
+ NULL
+};
+
+
+/* Create a new L2TP session. */
+ppp_pcb *pppol2tp_create(struct netif *pppif,
+ struct netif *netif, const ip_addr_t *ipaddr, u16_t port,
+ const u8_t *secret, u8_t secret_len,
+ ppp_link_status_cb_fn link_status_cb, void *ctx_cb) {
+ ppp_pcb *ppp;
+ pppol2tp_pcb *l2tp;
+ struct udp_pcb *udp;
+#if !PPPOL2TP_AUTH_SUPPORT
+ LWIP_UNUSED_ARG(secret);
+ LWIP_UNUSED_ARG(secret_len);
+#endif /* !PPPOL2TP_AUTH_SUPPORT */
+
+ if (ipaddr == NULL) {
+ goto ipaddr_check_failed;
+ }
+
+ l2tp = (pppol2tp_pcb *)LWIP_MEMPOOL_ALLOC(PPPOL2TP_PCB);
+ if (l2tp == NULL) {
+ goto memp_malloc_l2tp_failed;
+ }
+
+ udp = udp_new_ip_type(IP_GET_TYPE(ipaddr));
+ if (udp == NULL) {
+ goto udp_new_failed;
+ }
+ udp_recv(udp, pppol2tp_input, l2tp);
+
+ ppp = ppp_new(pppif, &pppol2tp_callbacks, l2tp, link_status_cb, ctx_cb);
+ if (ppp == NULL) {
+ goto ppp_new_failed;
+ }
+
+ memset(l2tp, 0, sizeof(pppol2tp_pcb));
+ l2tp->phase = PPPOL2TP_STATE_INITIAL;
+ l2tp->ppp = ppp;
+ l2tp->udp = udp;
+ l2tp->netif = netif;
+ ip_addr_copy(l2tp->remote_ip, *ipaddr);
+ l2tp->remote_port = port;
+#if PPPOL2TP_AUTH_SUPPORT
+ l2tp->secret = secret;
+ l2tp->secret_len = secret_len;
+#endif /* PPPOL2TP_AUTH_SUPPORT */
+
+ return ppp;
+
+ppp_new_failed:
+ udp_remove(udp);
+udp_new_failed:
+ LWIP_MEMPOOL_FREE(PPPOL2TP_PCB, l2tp);
+memp_malloc_l2tp_failed:
+ipaddr_check_failed:
+ return NULL;
+}
+
+/* Called by PPP core */
+static err_t pppol2tp_write(ppp_pcb *ppp, void *ctx, struct pbuf *p) {
+ pppol2tp_pcb *l2tp = (pppol2tp_pcb *)ctx;
+ struct pbuf *ph; /* UDP + L2TP header */
+ err_t ret;
+#if MIB2_STATS
+ u16_t tot_len;
+#else /* MIB2_STATS */
+ LWIP_UNUSED_ARG(ppp);
+#endif /* MIB2_STATS */
+
+ ph = pbuf_alloc(PBUF_TRANSPORT, (u16_t)(PPPOL2TP_OUTPUT_DATA_HEADER_LEN), PBUF_RAM);
+ if(!ph) {
+ LINK_STATS_INC(link.memerr);
+ LINK_STATS_INC(link.proterr);
+ MIB2_STATS_NETIF_INC(ppp->netif, ifoutdiscards);
+ pbuf_free(p);
+ return ERR_MEM;
+ }
+
+ pbuf_header(ph, -(s16_t)PPPOL2TP_OUTPUT_DATA_HEADER_LEN); /* hide L2TP header */
+ pbuf_cat(ph, p);
+#if MIB2_STATS
+ tot_len = ph->tot_len;
+#endif /* MIB2_STATS */
+
+ ret = pppol2tp_xmit(l2tp, ph);
+ if (ret != ERR_OK) {
+ LINK_STATS_INC(link.err);
+ MIB2_STATS_NETIF_INC(ppp->netif, ifoutdiscards);
+ return ret;
+ }
+
+ MIB2_STATS_NETIF_ADD(ppp->netif, ifoutoctets, (u16_t)tot_len);
+ MIB2_STATS_NETIF_INC(ppp->netif, ifoutucastpkts);
+ LINK_STATS_INC(link.xmit);
+ return ERR_OK;
+}
+
+/* Called by PPP core */
+static err_t pppol2tp_netif_output(ppp_pcb *ppp, void *ctx, struct pbuf *p, u_short protocol) {
+ pppol2tp_pcb *l2tp = (pppol2tp_pcb *)ctx;
+ struct pbuf *pb;
+ u8_t *pl;
+ err_t err;
+#if MIB2_STATS
+ u16_t tot_len;
+#else /* MIB2_STATS */
+ LWIP_UNUSED_ARG(ppp);
+#endif /* MIB2_STATS */
+
+ /* @todo: try to use pbuf_header() here! */
+ pb = pbuf_alloc(PBUF_TRANSPORT, PPPOL2TP_OUTPUT_DATA_HEADER_LEN + sizeof(protocol), PBUF_RAM);
+ if(!pb) {
+ LINK_STATS_INC(link.memerr);
+ LINK_STATS_INC(link.proterr);
+ MIB2_STATS_NETIF_INC(ppp->netif, ifoutdiscards);
+ return ERR_MEM;
+ }
+
+ pbuf_header(pb, -(s16_t)PPPOL2TP_OUTPUT_DATA_HEADER_LEN);
+
+ pl = (u8_t*)pb->payload;
+ PUTSHORT(protocol, pl);
+
+ pbuf_chain(pb, p);
+#if MIB2_STATS
+ tot_len = pb->tot_len;
+#endif /* MIB2_STATS */
+
+ if( (err = pppol2tp_xmit(l2tp, pb)) != ERR_OK) {
+ LINK_STATS_INC(link.err);
+ MIB2_STATS_NETIF_INC(ppp->netif, ifoutdiscards);
+ return err;
+ }
+
+ MIB2_STATS_NETIF_ADD(ppp->netif, ifoutoctets, tot_len);
+ MIB2_STATS_NETIF_INC(ppp->netif, ifoutucastpkts);
+ LINK_STATS_INC(link.xmit);
+ return ERR_OK;
+}
+
+/* Destroy a L2TP control block */
+static err_t pppol2tp_destroy(ppp_pcb *ppp, void *ctx) {
+ pppol2tp_pcb *l2tp = (pppol2tp_pcb *)ctx;
+ LWIP_UNUSED_ARG(ppp);
+
+ sys_untimeout(pppol2tp_timeout, l2tp);
+ udp_remove(l2tp->udp);
+ LWIP_MEMPOOL_FREE(PPPOL2TP_PCB, l2tp);
+ return ERR_OK;
+}
+
+/* Be a LAC, connect to a LNS. */
+static void pppol2tp_connect(ppp_pcb *ppp, void *ctx) {
+ err_t err;
+ pppol2tp_pcb *l2tp = (pppol2tp_pcb *)ctx;
+ lcp_options *lcp_wo;
+ lcp_options *lcp_ao;
+#if PPP_IPV4_SUPPORT && VJ_SUPPORT
+ ipcp_options *ipcp_wo;
+ ipcp_options *ipcp_ao;
+#endif /* PPP_IPV4_SUPPORT && VJ_SUPPORT */
+
+ l2tp->tunnel_port = l2tp->remote_port;
+ l2tp->our_ns = 0;
+ l2tp->peer_nr = 0;
+ l2tp->peer_ns = 0;
+ l2tp->source_tunnel_id = 0;
+ l2tp->remote_tunnel_id = 0;
+ l2tp->source_session_id = 0;
+ l2tp->remote_session_id = 0;
+ /* l2tp->*_retried are cleared when used */
+
+ lcp_wo = &ppp->lcp_wantoptions;
+ lcp_wo->mru = PPPOL2TP_DEFMRU;
+ lcp_wo->neg_asyncmap = 0;
+ lcp_wo->neg_pcompression = 0;
+ lcp_wo->neg_accompression = 0;
+ lcp_wo->passive = 0;
+ lcp_wo->silent = 0;
+
+ lcp_ao = &ppp->lcp_allowoptions;
+ lcp_ao->mru = PPPOL2TP_DEFMRU;
+ lcp_ao->neg_asyncmap = 0;
+ lcp_ao->neg_pcompression = 0;
+ lcp_ao->neg_accompression = 0;
+
+#if PPP_IPV4_SUPPORT && VJ_SUPPORT
+ ipcp_wo = &ppp->ipcp_wantoptions;
+ ipcp_wo->neg_vj = 0;
+ ipcp_wo->old_vj = 0;
+
+ ipcp_ao = &ppp->ipcp_allowoptions;
+ ipcp_ao->neg_vj = 0;
+ ipcp_ao->old_vj = 0;
+#endif /* PPP_IPV4_SUPPORT && VJ_SUPPORT */
+
+ /* Listen to a random source port, we need to do that instead of using udp_connect()
+ * because the L2TP LNS might answer with its own random source port (!= 1701)
+ */
+#if LWIP_IPV6
+ if (IP_IS_V6_VAL(l2tp->udp->local_ip)) {
+ udp_bind(l2tp->udp, IP6_ADDR_ANY, 0);
+ } else
+#endif /* LWIP_IPV6 */
+ udp_bind(l2tp->udp, IP_ADDR_ANY, 0);
+
+#if PPPOL2TP_AUTH_SUPPORT
+ /* Generate random vector */
+ if (l2tp->secret != NULL) {
+ magic_random_bytes(l2tp->secret_rv, sizeof(l2tp->secret_rv));
+ }
+#endif /* PPPOL2TP_AUTH_SUPPORT */
+
+ do {
+ l2tp->remote_tunnel_id = magic();
+ } while(l2tp->remote_tunnel_id == 0);
+ /* save state, in case we fail to send SCCRQ */
+ l2tp->sccrq_retried = 0;
+ l2tp->phase = PPPOL2TP_STATE_SCCRQ_SENT;
+ if ((err = pppol2tp_send_sccrq(l2tp)) != 0) {
+ PPPDEBUG(LOG_DEBUG, ("pppol2tp: failed to send SCCRQ, error=%d\n", err));
+ }
+ sys_timeout(PPPOL2TP_CONTROL_TIMEOUT, pppol2tp_timeout, l2tp);
+}
+
+/* Disconnect */
+static void pppol2tp_disconnect(ppp_pcb *ppp, void *ctx) {
+ pppol2tp_pcb *l2tp = (pppol2tp_pcb *)ctx;
+
+ l2tp->our_ns++;
+ pppol2tp_send_stopccn(l2tp, l2tp->our_ns);
+
+ /* stop any timer, disconnect can be called while initiating is in progress */
+ sys_untimeout(pppol2tp_timeout, l2tp);
+ l2tp->phase = PPPOL2TP_STATE_INITIAL;
+ ppp_link_end(ppp); /* notify upper layers */
+}
+
+/* UDP Callback for incoming IPv4 L2TP frames */
+static void pppol2tp_input(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port) {
+ pppol2tp_pcb *l2tp = (pppol2tp_pcb*)arg;
+ u16_t hflags, hlen, len=0, tunnel_id=0, session_id=0, ns=0, nr=0, offset=0;
+ u8_t *inp;
+ LWIP_UNUSED_ARG(pcb);
+
+ /* we can't unbound a UDP pcb, thus we can still receive UDP frames after the link is closed */
+ if (l2tp->phase < PPPOL2TP_STATE_SCCRQ_SENT) {
+ goto free_and_return;
+ }
+
+ if (!ip_addr_cmp(&l2tp->remote_ip, addr)) {
+ goto free_and_return;
+ }
+
+ /* discard packet if port mismatch, but only if we received a SCCRP */
+ if (l2tp->phase > PPPOL2TP_STATE_SCCRQ_SENT && l2tp->tunnel_port != port) {
+ goto free_and_return;
+ }
+
+ /* printf("-----------\nL2TP INPUT, %d\n", p->len); */
+
+ /* L2TP header */
+ if (p->len < sizeof(hflags) + sizeof(tunnel_id) + sizeof(session_id) ) {
+ goto packet_too_short;
+ }
+
+ inp = (u8_t*)p->payload;
+ GETSHORT(hflags, inp);
+
+ if (hflags & PPPOL2TP_HEADERFLAG_CONTROL) {
+ /* check mandatory flags for a control packet */
+ if ( (hflags & PPPOL2TP_HEADERFLAG_CONTROL_MANDATORY) != PPPOL2TP_HEADERFLAG_CONTROL_MANDATORY ) {
+ PPPDEBUG(LOG_DEBUG, ("pppol2tp: mandatory header flags for control packet not set\n"));
+ goto free_and_return;
+ }
+ /* check forbidden flags for a control packet */
+ if (hflags & PPPOL2TP_HEADERFLAG_CONTROL_FORBIDDEN) {
+ PPPDEBUG(LOG_DEBUG, ("pppol2tp: forbidden header flags for control packet found\n"));
+ goto free_and_return;
+ }
+ } else {
+ /* check mandatory flags for a data packet */
+ if ( (hflags & PPPOL2TP_HEADERFLAG_DATA_MANDATORY) != PPPOL2TP_HEADERFLAG_DATA_MANDATORY) {
+ PPPDEBUG(LOG_DEBUG, ("pppol2tp: mandatory header flags for data packet not set\n"));
+ goto free_and_return;
+ }
+ }
+
+ /* Expected header size */
+ hlen = sizeof(hflags) + sizeof(tunnel_id) + sizeof(session_id);
+ if (hflags & PPPOL2TP_HEADERFLAG_LENGTH) {
+ hlen += sizeof(len);
+ }
+ if (hflags & PPPOL2TP_HEADERFLAG_SEQUENCE) {
+ hlen += sizeof(ns) + sizeof(nr);
+ }
+ if (hflags & PPPOL2TP_HEADERFLAG_OFFSET) {
+ hlen += sizeof(offset);
+ }
+ if (p->len < hlen) {
+ goto packet_too_short;
+ }
+
+ if (hflags & PPPOL2TP_HEADERFLAG_LENGTH) {
+ GETSHORT(len, inp);
+ if (p->len < len || len < hlen) {
+ goto packet_too_short;
+ }
+ }
+ GETSHORT(tunnel_id, inp);
+ GETSHORT(session_id, inp);
+ if (hflags & PPPOL2TP_HEADERFLAG_SEQUENCE) {
+ GETSHORT(ns, inp);
+ GETSHORT(nr, inp);
+ }
+ if (hflags & PPPOL2TP_HEADERFLAG_OFFSET) {
+ GETSHORT(offset, inp)
+ if (offset > 4096) { /* don't be fooled with large offset which might overflow hlen */
+ PPPDEBUG(LOG_DEBUG, ("pppol2tp: strange packet received, offset=%d\n", offset));
+ goto free_and_return;
+ }
+ hlen += offset;
+ if (p->len < hlen) {
+ goto packet_too_short;
+ }
+ INCPTR(offset, inp);
+ }
+
+ /* printf("HLEN = %d\n", hlen); */
+
+ /* skip L2TP header */
+ if (pbuf_header(p, -(s16_t)hlen) != 0) {
+ goto free_and_return;
+ }
+
+ /* printf("LEN=%d, TUNNEL_ID=%d, SESSION_ID=%d, NS=%d, NR=%d, OFFSET=%d\n", len, tunnel_id, session_id, ns, nr, offset); */
+ PPPDEBUG(LOG_DEBUG, ("pppol2tp: input packet, len=%"U16_F", tunnel=%"U16_F", session=%"U16_F", ns=%"U16_F", nr=%"U16_F"\n",
+ len, tunnel_id, session_id, ns, nr));
+
+ /* Control packet */
+ if (hflags & PPPOL2TP_HEADERFLAG_CONTROL) {
+ pppol2tp_dispatch_control_packet(l2tp, port, p, ns, nr);
+ goto free_and_return;
+ }
+
+ /* Data packet */
+ if(l2tp->phase != PPPOL2TP_STATE_DATA) {
+ goto free_and_return;
+ }
+ if(tunnel_id != l2tp->remote_tunnel_id) {
+ PPPDEBUG(LOG_DEBUG, ("pppol2tp: tunnel ID mismatch, assigned=%d, received=%d\n", l2tp->remote_tunnel_id, tunnel_id));
+ goto free_and_return;
+ }
+ if(session_id != l2tp->remote_session_id) {
+ PPPDEBUG(LOG_DEBUG, ("pppol2tp: session ID mismatch, assigned=%d, received=%d\n", l2tp->remote_session_id, session_id));
+ goto free_and_return;
+ }
+ /*
+ * skip address & flags if necessary
+ *
+ * RFC 2661 does not specify whether the PPP frame in the L2TP payload should
+ * have a HDLC header or not. We handle both cases for compatibility.
+ */
+ if (p->len >= 2) {
+ GETSHORT(hflags, inp);
+ if (hflags == 0xff03) {
+ pbuf_header(p, -(s16_t)2);
+ }
+ }
+ /* Dispatch the packet thereby consuming it. */
+ ppp_input(l2tp->ppp, p);
+ return;
+
+packet_too_short:
+ PPPDEBUG(LOG_DEBUG, ("pppol2tp: packet too short: %d\n", p->len));
+free_and_return:
+ pbuf_free(p);
+}
+
+/* L2TP Control packet entry point */
+static void pppol2tp_dispatch_control_packet(pppol2tp_pcb *l2tp, u16_t port, struct pbuf *p, u16_t ns, u16_t nr) {
+ u8_t *inp;
+ u16_t avplen, avpflags, vendorid, attributetype, messagetype=0;
+ err_t err;
+#if PPPOL2TP_AUTH_SUPPORT
+ lwip_md5_context md5_ctx;
+ u8_t md5_hash[16];
+ u8_t challenge_id = 0;
+#endif /* PPPOL2TP_AUTH_SUPPORT */
+
+ l2tp->peer_nr = nr;
+ l2tp->peer_ns = ns;
+ /* printf("L2TP CTRL INPUT, ns=%d, nr=%d, len=%d\n", ns, nr, p->len); */
+
+ /* Handle the special case of the ICCN acknowledge */
+ if (l2tp->phase == PPPOL2TP_STATE_ICCN_SENT && l2tp->peer_nr > l2tp->our_ns) {
+ l2tp->phase = PPPOL2TP_STATE_DATA;
+ }
+
+ /* ZLB packets */
+ if (p->tot_len == 0) {
+ return;
+ }
+
+ p = ppp_singlebuf(p);
+ inp = (u8_t*)p->payload;
+ /* Decode AVPs */
+ while (p->len > 0) {
+ if (p->len < sizeof(avpflags) + sizeof(vendorid) + sizeof(attributetype) ) {
+ goto packet_too_short;
+ }
+ GETSHORT(avpflags, inp);
+ avplen = avpflags & PPPOL2TP_AVPHEADERFLAG_LENGTHMASK;
+ /* printf("AVPLEN = %d\n", avplen); */
+ if (p->len < avplen || avplen < sizeof(avpflags) + sizeof(vendorid) + sizeof(attributetype)) {
+ goto packet_too_short;
+ }
+ GETSHORT(vendorid, inp);
+ GETSHORT(attributetype, inp);
+ avplen -= sizeof(avpflags) + sizeof(vendorid) + sizeof(attributetype);
+
+ /* Message type must be the first AVP */
+ if (messagetype == 0) {
+ if (attributetype != 0 || vendorid != 0 || avplen != sizeof(messagetype) ) {
+ PPPDEBUG(LOG_DEBUG, ("pppol2tp: message type must be the first AVP\n"));
+ return;
+ }
+ GETSHORT(messagetype, inp);
+ /* printf("Message type = %d\n", messagetype); */
+ switch(messagetype) {
+ /* Start Control Connection Reply */
+ case PPPOL2TP_MESSAGETYPE_SCCRP:
+ /* Only accept SCCRP packet if we sent a SCCRQ */
+ if (l2tp->phase != PPPOL2TP_STATE_SCCRQ_SENT) {
+ goto send_zlb;
+ }
+ break;
+ /* Incoming Call Reply */
+ case PPPOL2TP_MESSAGETYPE_ICRP:
+ /* Only accept ICRP packet if we sent a IRCQ */
+ if (l2tp->phase != PPPOL2TP_STATE_ICRQ_SENT) {
+ goto send_zlb;
+ }
+ break;
+ /* Stop Control Connection Notification */
+ case PPPOL2TP_MESSAGETYPE_STOPCCN:
+ pppol2tp_send_zlb(l2tp, l2tp->our_ns); /* Ack the StopCCN before we switch to down state */
+ if (l2tp->phase < PPPOL2TP_STATE_DATA) {
+ pppol2tp_abort_connect(l2tp);
+ } else if (l2tp->phase == PPPOL2TP_STATE_DATA) {
+ /* Don't disconnect here, we let the LCP Echo/Reply find the fact
+ * that PPP session is down. Asking the PPP stack to end the session
+ * require strict checking about the PPP phase to prevent endless
+ * disconnection loops.
+ */
+ }
+ return;
+ default:
+ break;
+ }
+ goto nextavp;
+ }
+
+ /* Skip proprietary L2TP extensions */
+ if (vendorid != 0) {
+ goto skipavp;
+ }
+
+ switch (messagetype) {
+ /* Start Control Connection Reply */
+ case PPPOL2TP_MESSAGETYPE_SCCRP:
+ switch (attributetype) {
+ case PPPOL2TP_AVPTYPE_TUNNELID:
+ if (avplen != sizeof(l2tp->source_tunnel_id) ) {
+ PPPDEBUG(LOG_DEBUG, ("pppol2tp: AVP Assign tunnel ID length check failed\n"));
+ return;
+ }
+ GETSHORT(l2tp->source_tunnel_id, inp);
+ PPPDEBUG(LOG_DEBUG, ("pppol2tp: Assigned tunnel ID %"U16_F"\n", l2tp->source_tunnel_id));
+ goto nextavp;
+#if PPPOL2TP_AUTH_SUPPORT
+ case PPPOL2TP_AVPTYPE_CHALLENGE:
+ if (avplen == 0) {
+ PPPDEBUG(LOG_DEBUG, ("pppol2tp: Challenge length check failed\n"));
+ return;
+ }
+ if (l2tp->secret == NULL) {
+ PPPDEBUG(LOG_DEBUG, ("pppol2tp: Received challenge from peer and no secret key available\n"));
+ pppol2tp_abort_connect(l2tp);
+ return;
+ }
+ /* Generate hash of ID, secret, challenge */
+ lwip_md5_init(&md5_ctx);
+ lwip_md5_starts(&md5_ctx);
+ challenge_id = PPPOL2TP_MESSAGETYPE_SCCCN;
+ lwip_md5_update(&md5_ctx, &challenge_id, 1);
+ lwip_md5_update(&md5_ctx, l2tp->secret, l2tp->secret_len);
+ lwip_md5_update(&md5_ctx, inp, avplen);
+ lwip_md5_finish(&md5_ctx, l2tp->challenge_hash);
+ lwip_md5_free(&md5_ctx);
+ l2tp->send_challenge = 1;
+ goto skipavp;
+ case PPPOL2TP_AVPTYPE_CHALLENGERESPONSE:
+ if (avplen != PPPOL2TP_AVPTYPE_CHALLENGERESPONSE_SIZE) {
+ PPPDEBUG(LOG_DEBUG, ("pppol2tp: AVP Challenge Response length check failed\n"));
+ return;
+ }
+ /* Generate hash of ID, secret, challenge */
+ lwip_md5_init(&md5_ctx);
+ lwip_md5_starts(&md5_ctx);
+ challenge_id = PPPOL2TP_MESSAGETYPE_SCCRP;
+ lwip_md5_update(&md5_ctx, &challenge_id, 1);
+ lwip_md5_update(&md5_ctx, l2tp->secret, l2tp->secret_len);
+ lwip_md5_update(&md5_ctx, l2tp->secret_rv, sizeof(l2tp->secret_rv));
+ lwip_md5_finish(&md5_ctx, md5_hash);
+ lwip_md5_free(&md5_ctx);
+ if ( memcmp(inp, md5_hash, sizeof(md5_hash)) ) {
+ PPPDEBUG(LOG_DEBUG, ("pppol2tp: Received challenge response from peer and secret key do not match\n"));
+ pppol2tp_abort_connect(l2tp);
+ return;
+ }
+ goto skipavp;
+#endif /* PPPOL2TP_AUTH_SUPPORT */
+ default:
+ break;
+ }
+ break;
+ /* Incoming Call Reply */
+ case PPPOL2TP_MESSAGETYPE_ICRP:
+ switch (attributetype) {
+ case PPPOL2TP_AVPTYPE_SESSIONID:
+ if (avplen != sizeof(l2tp->source_session_id) ) {
+ PPPDEBUG(LOG_DEBUG, ("pppol2tp: AVP Assign session ID length check failed\n"));
+ return;
+ }
+ GETSHORT(l2tp->source_session_id, inp);
+ PPPDEBUG(LOG_DEBUG, ("pppol2tp: Assigned session ID %"U16_F"\n", l2tp->source_session_id));
+ goto nextavp;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+skipavp:
+ INCPTR(avplen, inp);
+nextavp:
+ /* printf("AVP Found, vendor=%d, attribute=%d, len=%d\n", vendorid, attributetype, avplen); */
+ /* next AVP */
+ if (pbuf_header(p, -(s16_t)(avplen + sizeof(avpflags) + sizeof(vendorid) + sizeof(attributetype)) ) != 0) {
+ return;
+ }
+ }
+
+ switch(messagetype) {
+ /* Start Control Connection Reply */
+ case PPPOL2TP_MESSAGETYPE_SCCRP:
+ do {
+ l2tp->remote_session_id = magic();
+ } while(l2tp->remote_session_id == 0);
+ l2tp->tunnel_port = port; /* LNS server might have chosen its own local port */
+ l2tp->icrq_retried = 0;
+ l2tp->phase = PPPOL2TP_STATE_ICRQ_SENT;
+ l2tp->our_ns++;
+ if ((err = pppol2tp_send_scccn(l2tp, l2tp->our_ns)) != 0) {
+ PPPDEBUG(LOG_DEBUG, ("pppol2tp: failed to send SCCCN, error=%d\n", err));
+ }
+ l2tp->our_ns++;
+ if ((err = pppol2tp_send_icrq(l2tp, l2tp->our_ns)) != 0) {
+ PPPDEBUG(LOG_DEBUG, ("pppol2tp: failed to send ICRQ, error=%d\n", err));
+ }
+ sys_untimeout(pppol2tp_timeout, l2tp);
+ sys_timeout(PPPOL2TP_CONTROL_TIMEOUT, pppol2tp_timeout, l2tp);
+ break;
+ /* Incoming Call Reply */
+ case PPPOL2TP_MESSAGETYPE_ICRP:
+ l2tp->iccn_retried = 0;
+ l2tp->phase = PPPOL2TP_STATE_ICCN_SENT;
+ l2tp->our_ns++;
+ ppp_start(l2tp->ppp); /* notify upper layers */
+ if ((err = pppol2tp_send_iccn(l2tp, l2tp->our_ns)) != 0) {
+ PPPDEBUG(LOG_DEBUG, ("pppol2tp: failed to send ICCN, error=%d\n", err));
+ }
+ sys_untimeout(pppol2tp_timeout, l2tp);
+ sys_timeout(PPPOL2TP_CONTROL_TIMEOUT, pppol2tp_timeout, l2tp);
+ break;
+ /* Unhandled packet, send ZLB ACK */
+ default:
+ goto send_zlb;
+ }
+ return;
+
+send_zlb:
+ pppol2tp_send_zlb(l2tp, l2tp->our_ns);
+ return;
+packet_too_short:
+ PPPDEBUG(LOG_DEBUG, ("pppol2tp: packet too short: %d\n", p->len));
+}
+
+/* L2TP Timeout handler */
+static void pppol2tp_timeout(void *arg) {
+ pppol2tp_pcb *l2tp = (pppol2tp_pcb*)arg;
+ err_t err;
+ u32_t retry_wait;
+
+ PPPDEBUG(LOG_DEBUG, ("pppol2tp: timeout\n"));
+
+ switch (l2tp->phase) {
+ case PPPOL2TP_STATE_SCCRQ_SENT:
+ /* backoff wait */
+ if (l2tp->sccrq_retried < 0xff) {
+ l2tp->sccrq_retried++;
+ }
+ if (!l2tp->ppp->settings.persist && l2tp->sccrq_retried >= PPPOL2TP_MAXSCCRQ) {
+ pppol2tp_abort_connect(l2tp);
+ return;
+ }
+ retry_wait = LWIP_MIN(PPPOL2TP_CONTROL_TIMEOUT * l2tp->sccrq_retried, PPPOL2TP_SLOW_RETRY);
+ PPPDEBUG(LOG_DEBUG, ("pppol2tp: sccrq_retried=%d\n", l2tp->sccrq_retried));
+ if ((err = pppol2tp_send_sccrq(l2tp)) != 0) {
+ l2tp->sccrq_retried--;
+ PPPDEBUG(LOG_DEBUG, ("pppol2tp: failed to send SCCRQ, error=%d\n", err));
+ }
+ sys_timeout(retry_wait, pppol2tp_timeout, l2tp);
+ break;
+
+ case PPPOL2TP_STATE_ICRQ_SENT:
+ l2tp->icrq_retried++;
+ if (l2tp->icrq_retried >= PPPOL2TP_MAXICRQ) {
+ pppol2tp_abort_connect(l2tp);
+ return;
+ }
+ PPPDEBUG(LOG_DEBUG, ("pppol2tp: icrq_retried=%d\n", l2tp->icrq_retried));
+ if (l2tp->peer_nr <= l2tp->our_ns -1) { /* the SCCCN was not acknowledged */
+ if ((err = pppol2tp_send_scccn(l2tp, l2tp->our_ns -1)) != 0) {
+ l2tp->icrq_retried--;
+ PPPDEBUG(LOG_DEBUG, ("pppol2tp: failed to send SCCCN, error=%d\n", err));
+ sys_timeout(PPPOL2TP_CONTROL_TIMEOUT, pppol2tp_timeout, l2tp);
+ break;
+ }
+ }
+ if ((err = pppol2tp_send_icrq(l2tp, l2tp->our_ns)) != 0) {
+ l2tp->icrq_retried--;
+ PPPDEBUG(LOG_DEBUG, ("pppol2tp: failed to send ICRQ, error=%d\n", err));
+ }
+ sys_timeout(PPPOL2TP_CONTROL_TIMEOUT, pppol2tp_timeout, l2tp);
+ break;
+
+ case PPPOL2TP_STATE_ICCN_SENT:
+ l2tp->iccn_retried++;
+ if (l2tp->iccn_retried >= PPPOL2TP_MAXICCN) {
+ pppol2tp_abort_connect(l2tp);
+ return;
+ }
+ PPPDEBUG(LOG_DEBUG, ("pppol2tp: iccn_retried=%d\n", l2tp->iccn_retried));
+ if ((err = pppol2tp_send_iccn(l2tp, l2tp->our_ns)) != 0) {
+ l2tp->iccn_retried--;
+ PPPDEBUG(LOG_DEBUG, ("pppol2tp: failed to send ICCN, error=%d\n", err));
+ }
+ sys_timeout(PPPOL2TP_CONTROL_TIMEOUT, pppol2tp_timeout, l2tp);
+ break;
+
+ default:
+ return; /* all done, work in peace */
+ }
+}
+
+/* Connection attempt aborted */
+static void pppol2tp_abort_connect(pppol2tp_pcb *l2tp) {
+ PPPDEBUG(LOG_DEBUG, ("pppol2tp: could not establish connection\n"));
+ l2tp->phase = PPPOL2TP_STATE_INITIAL;
+ ppp_link_failed(l2tp->ppp); /* notify upper layers */
+}
+
+/* Initiate a new tunnel */
+static err_t pppol2tp_send_sccrq(pppol2tp_pcb *l2tp) {
+ struct pbuf *pb;
+ u8_t *p;
+ u16_t len;
+
+ /* calculate UDP packet length */
+ len = 12 +8 +8 +10 +10 +6+sizeof(PPPOL2TP_HOSTNAME)-1 +6+sizeof(PPPOL2TP_VENDORNAME)-1 +8 +8;
+#if PPPOL2TP_AUTH_SUPPORT
+ if (l2tp->secret != NULL) {
+ len += 6 + sizeof(l2tp->secret_rv);
+ }
+#endif /* PPPOL2TP_AUTH_SUPPORT */
+
+ /* allocate a buffer */
+ pb = pbuf_alloc(PBUF_TRANSPORT, len, PBUF_RAM);
+ if (pb == NULL) {
+ return ERR_MEM;
+ }
+ LWIP_ASSERT("pb->tot_len == pb->len", pb->tot_len == pb->len);
+
+ p = (u8_t*)pb->payload;
+ /* fill in pkt */
+ /* L2TP control header */
+ PUTSHORT(PPPOL2TP_HEADERFLAG_CONTROL_MANDATORY, p);
+ PUTSHORT(len, p); /* Length */
+ PUTSHORT(0, p); /* Tunnel Id */
+ PUTSHORT(0, p); /* Session Id */
+ PUTSHORT(0, p); /* NS Sequence number - to peer */
+ PUTSHORT(0, p); /* NR Sequence number - expected for peer */
+
+ /* AVP - Message type */
+ PUTSHORT(PPPOL2TP_AVPHEADERFLAG_MANDATORY + 8, p); /* Mandatory flag + len field */
+ PUTSHORT(0, p); /* Vendor ID */
+ PUTSHORT(PPPOL2TP_AVPTYPE_MESSAGE, p); /* Attribute type: Message Type */
+ PUTSHORT(PPPOL2TP_MESSAGETYPE_SCCRQ, p); /* Attribute value: Message type: SCCRQ */
+
+ /* AVP - L2TP Version */
+ PUTSHORT(PPPOL2TP_AVPHEADERFLAG_MANDATORY + 8, p); /* Mandatory flag + len field */
+ PUTSHORT(0, p); /* Vendor ID */
+ PUTSHORT(PPPOL2TP_AVPTYPE_VERSION, p); /* Attribute type: Version */
+ PUTSHORT(PPPOL2TP_VERSION, p); /* Attribute value: L2TP Version */
+
+ /* AVP - Framing capabilities */
+ PUTSHORT(PPPOL2TP_AVPHEADERFLAG_MANDATORY + 10, p); /* Mandatory flag + len field */
+ PUTSHORT(0, p); /* Vendor ID */
+ PUTSHORT(PPPOL2TP_AVPTYPE_FRAMINGCAPABILITIES, p); /* Attribute type: Framing capabilities */
+ PUTLONG(PPPOL2TP_FRAMINGCAPABILITIES, p); /* Attribute value: Framing capabilities */
+
+ /* AVP - Bearer capabilities */
+ PUTSHORT(PPPOL2TP_AVPHEADERFLAG_MANDATORY + 10, p); /* Mandatory flag + len field */
+ PUTSHORT(0, p); /* Vendor ID */
+ PUTSHORT(PPPOL2TP_AVPTYPE_BEARERCAPABILITIES, p); /* Attribute type: Bearer capabilities */
+ PUTLONG(PPPOL2TP_BEARERCAPABILITIES, p); /* Attribute value: Bearer capabilities */
+
+ /* AVP - Host name */
+ PUTSHORT(PPPOL2TP_AVPHEADERFLAG_MANDATORY + 6+sizeof(PPPOL2TP_HOSTNAME)-1, p); /* Mandatory flag + len field */
+ PUTSHORT(0, p); /* Vendor ID */
+ PUTSHORT(PPPOL2TP_AVPTYPE_HOSTNAME, p); /* Attribute type: Hostname */
+ MEMCPY(p, PPPOL2TP_HOSTNAME, sizeof(PPPOL2TP_HOSTNAME)-1); /* Attribute value: Hostname */
+ INCPTR(sizeof(PPPOL2TP_HOSTNAME)-1, p);
+
+ /* AVP - Vendor name */
+ PUTSHORT(6+sizeof(PPPOL2TP_VENDORNAME)-1, p); /* len field */
+ PUTSHORT(0, p); /* Vendor ID */
+ PUTSHORT(PPPOL2TP_AVPTYPE_VENDORNAME, p); /* Attribute type: Vendor name */
+ MEMCPY(p, PPPOL2TP_VENDORNAME, sizeof(PPPOL2TP_VENDORNAME)-1); /* Attribute value: Vendor name */
+ INCPTR(sizeof(PPPOL2TP_VENDORNAME)-1, p);
+
+ /* AVP - Assign tunnel ID */
+ PUTSHORT(PPPOL2TP_AVPHEADERFLAG_MANDATORY + 8, p); /* Mandatory flag + len field */
+ PUTSHORT(0, p); /* Vendor ID */
+ PUTSHORT(PPPOL2TP_AVPTYPE_TUNNELID, p); /* Attribute type: Tunnel ID */
+ PUTSHORT(l2tp->remote_tunnel_id, p); /* Attribute value: Tunnel ID */
+
+ /* AVP - Receive window size */
+ PUTSHORT(PPPOL2TP_AVPHEADERFLAG_MANDATORY + 8, p); /* Mandatory flag + len field */
+ PUTSHORT(0, p); /* Vendor ID */
+ PUTSHORT(PPPOL2TP_AVPTYPE_RECEIVEWINDOWSIZE, p); /* Attribute type: Receive window size */
+ PUTSHORT(PPPOL2TP_RECEIVEWINDOWSIZE, p); /* Attribute value: Receive window size */
+
+#if PPPOL2TP_AUTH_SUPPORT
+ /* AVP - Challenge */
+ if (l2tp->secret != NULL) {
+ PUTSHORT(PPPOL2TP_AVPHEADERFLAG_MANDATORY + 6 + sizeof(l2tp->secret_rv), p); /* Mandatory flag + len field */
+ PUTSHORT(0, p); /* Vendor ID */
+ PUTSHORT(PPPOL2TP_AVPTYPE_CHALLENGE, p); /* Attribute type: Challenge */
+ MEMCPY(p, l2tp->secret_rv, sizeof(l2tp->secret_rv)); /* Attribute value: Random vector */
+ INCPTR(sizeof(l2tp->secret_rv), p);
+ }
+#endif /* PPPOL2TP_AUTH_SUPPORT */
+
+ return pppol2tp_udp_send(l2tp, pb);
+}
+
+/* Complete tunnel establishment */
+static err_t pppol2tp_send_scccn(pppol2tp_pcb *l2tp, u16_t ns) {
+ struct pbuf *pb;
+ u8_t *p;
+ u16_t len;
+
+ /* calculate UDP packet length */
+ len = 12 +8;
+#if PPPOL2TP_AUTH_SUPPORT
+ if (l2tp->send_challenge) {
+ len += 6 + sizeof(l2tp->challenge_hash);
+ }
+#endif /* PPPOL2TP_AUTH_SUPPORT */
+
+ /* allocate a buffer */
+ pb = pbuf_alloc(PBUF_TRANSPORT, len, PBUF_RAM);
+ if (pb == NULL) {
+ return ERR_MEM;
+ }
+ LWIP_ASSERT("pb->tot_len == pb->len", pb->tot_len == pb->len);
+
+ p = (u8_t*)pb->payload;
+ /* fill in pkt */
+ /* L2TP control header */
+ PUTSHORT(PPPOL2TP_HEADERFLAG_CONTROL_MANDATORY, p);
+ PUTSHORT(len, p); /* Length */
+ PUTSHORT(l2tp->source_tunnel_id, p); /* Tunnel Id */
+ PUTSHORT(0, p); /* Session Id */
+ PUTSHORT(ns, p); /* NS Sequence number - to peer */
+ PUTSHORT(l2tp->peer_ns+1, p); /* NR Sequence number - expected for peer */
+
+ /* AVP - Message type */
+ PUTSHORT(PPPOL2TP_AVPHEADERFLAG_MANDATORY + 8, p); /* Mandatory flag + len field */
+ PUTSHORT(0, p); /* Vendor ID */
+ PUTSHORT(PPPOL2TP_AVPTYPE_MESSAGE, p); /* Attribute type: Message Type */
+ PUTSHORT(PPPOL2TP_MESSAGETYPE_SCCCN, p); /* Attribute value: Message type: SCCCN */
+
+#if PPPOL2TP_AUTH_SUPPORT
+ /* AVP - Challenge response */
+ if (l2tp->send_challenge) {
+ PUTSHORT(PPPOL2TP_AVPHEADERFLAG_MANDATORY + 6 + sizeof(l2tp->challenge_hash), p); /* Mandatory flag + len field */
+ PUTSHORT(0, p); /* Vendor ID */
+ PUTSHORT(PPPOL2TP_AVPTYPE_CHALLENGERESPONSE, p); /* Attribute type: Challenge response */
+ MEMCPY(p, l2tp->challenge_hash, sizeof(l2tp->challenge_hash)); /* Attribute value: Computed challenge */
+ INCPTR(sizeof(l2tp->challenge_hash), p);
+ }
+#endif /* PPPOL2TP_AUTH_SUPPORT */
+
+ return pppol2tp_udp_send(l2tp, pb);
+}
+
+/* Initiate a new session */
+static err_t pppol2tp_send_icrq(pppol2tp_pcb *l2tp, u16_t ns) {
+ struct pbuf *pb;
+ u8_t *p;
+ u16_t len;
+ u32_t serialnumber;
+
+ /* calculate UDP packet length */
+ len = 12 +8 +8 +10;
+
+ /* allocate a buffer */
+ pb = pbuf_alloc(PBUF_TRANSPORT, len, PBUF_RAM);
+ if (pb == NULL) {
+ return ERR_MEM;
+ }
+ LWIP_ASSERT("pb->tot_len == pb->len", pb->tot_len == pb->len);
+
+ p = (u8_t*)pb->payload;
+ /* fill in pkt */
+ /* L2TP control header */
+ PUTSHORT(PPPOL2TP_HEADERFLAG_CONTROL_MANDATORY, p);
+ PUTSHORT(len, p); /* Length */
+ PUTSHORT(l2tp->source_tunnel_id, p); /* Tunnel Id */
+ PUTSHORT(0, p); /* Session Id */
+ PUTSHORT(ns, p); /* NS Sequence number - to peer */
+ PUTSHORT(l2tp->peer_ns+1, p); /* NR Sequence number - expected for peer */
+
+ /* AVP - Message type */
+ PUTSHORT(PPPOL2TP_AVPHEADERFLAG_MANDATORY + 8, p); /* Mandatory flag + len field */
+ PUTSHORT(0, p); /* Vendor ID */
+ PUTSHORT(PPPOL2TP_AVPTYPE_MESSAGE, p); /* Attribute type: Message Type */
+ PUTSHORT(PPPOL2TP_MESSAGETYPE_ICRQ, p); /* Attribute value: Message type: ICRQ */
+
+ /* AVP - Assign session ID */
+ PUTSHORT(PPPOL2TP_AVPHEADERFLAG_MANDATORY + 8, p); /* Mandatory flag + len field */
+ PUTSHORT(0, p); /* Vendor ID */
+ PUTSHORT(PPPOL2TP_AVPTYPE_SESSIONID, p); /* Attribute type: Session ID */
+ PUTSHORT(l2tp->remote_session_id, p); /* Attribute value: Session ID */
+
+ /* AVP - Call Serial Number */
+ PUTSHORT(PPPOL2TP_AVPHEADERFLAG_MANDATORY + 10, p); /* Mandatory flag + len field */
+ PUTSHORT(0, p); /* Vendor ID */
+ PUTSHORT(PPPOL2TP_AVPTYPE_CALLSERIALNUMBER, p); /* Attribute type: Serial number */
+ serialnumber = magic();
+ PUTLONG(serialnumber, p); /* Attribute value: Serial number */
+
+ return pppol2tp_udp_send(l2tp, pb);
+}
+
+/* Complete tunnel establishment */
+static err_t pppol2tp_send_iccn(pppol2tp_pcb *l2tp, u16_t ns) {
+ struct pbuf *pb;
+ u8_t *p;
+ u16_t len;
+
+ /* calculate UDP packet length */
+ len = 12 +8 +10 +10;
+
+ /* allocate a buffer */
+ pb = pbuf_alloc(PBUF_TRANSPORT, len, PBUF_RAM);
+ if (pb == NULL) {
+ return ERR_MEM;
+ }
+ LWIP_ASSERT("pb->tot_len == pb->len", pb->tot_len == pb->len);
+
+ p = (u8_t*)pb->payload;
+ /* fill in pkt */
+ /* L2TP control header */
+ PUTSHORT(PPPOL2TP_HEADERFLAG_CONTROL_MANDATORY, p);
+ PUTSHORT(len, p); /* Length */
+ PUTSHORT(l2tp->source_tunnel_id, p); /* Tunnel Id */
+ PUTSHORT(l2tp->source_session_id, p); /* Session Id */
+ PUTSHORT(ns, p); /* NS Sequence number - to peer */
+ PUTSHORT(l2tp->peer_ns+1, p); /* NR Sequence number - expected for peer */
+
+ /* AVP - Message type */
+ PUTSHORT(PPPOL2TP_AVPHEADERFLAG_MANDATORY + 8, p); /* Mandatory flag + len field */
+ PUTSHORT(0, p); /* Vendor ID */
+ PUTSHORT(PPPOL2TP_AVPTYPE_MESSAGE, p); /* Attribute type: Message Type */
+ PUTSHORT(PPPOL2TP_MESSAGETYPE_ICCN, p); /* Attribute value: Message type: ICCN */
+
+ /* AVP - Framing type */
+ PUTSHORT(PPPOL2TP_AVPHEADERFLAG_MANDATORY + 10, p); /* Mandatory flag + len field */
+ PUTSHORT(0, p); /* Vendor ID */
+ PUTSHORT(PPPOL2TP_AVPTYPE_FRAMINGTYPE, p); /* Attribute type: Framing type */
+ PUTLONG(PPPOL2TP_FRAMINGTYPE, p); /* Attribute value: Framing type */
+
+ /* AVP - TX Connect speed */
+ PUTSHORT(PPPOL2TP_AVPHEADERFLAG_MANDATORY + 10, p); /* Mandatory flag + len field */
+ PUTSHORT(0, p); /* Vendor ID */
+ PUTSHORT(PPPOL2TP_AVPTYPE_TXCONNECTSPEED, p); /* Attribute type: TX Connect speed */
+ PUTLONG(PPPOL2TP_TXCONNECTSPEED, p); /* Attribute value: TX Connect speed */
+
+ return pppol2tp_udp_send(l2tp, pb);
+}
+
+/* Send a ZLB ACK packet */
+static err_t pppol2tp_send_zlb(pppol2tp_pcb *l2tp, u16_t ns) {
+ struct pbuf *pb;
+ u8_t *p;
+ u16_t len;
+
+ /* calculate UDP packet length */
+ len = 12;
+
+ /* allocate a buffer */
+ pb = pbuf_alloc(PBUF_TRANSPORT, len, PBUF_RAM);
+ if (pb == NULL) {
+ return ERR_MEM;
+ }
+ LWIP_ASSERT("pb->tot_len == pb->len", pb->tot_len == pb->len);
+
+ p = (u8_t*)pb->payload;
+ /* fill in pkt */
+ /* L2TP control header */
+ PUTSHORT(PPPOL2TP_HEADERFLAG_CONTROL_MANDATORY, p);
+ PUTSHORT(len, p); /* Length */
+ PUTSHORT(l2tp->source_tunnel_id, p); /* Tunnel Id */
+ PUTSHORT(0, p); /* Session Id */
+ PUTSHORT(ns, p); /* NS Sequence number - to peer */
+ PUTSHORT(l2tp->peer_ns+1, p); /* NR Sequence number - expected for peer */
+
+ return pppol2tp_udp_send(l2tp, pb);
+}
+
+/* Send a StopCCN packet */
+static err_t pppol2tp_send_stopccn(pppol2tp_pcb *l2tp, u16_t ns) {
+ struct pbuf *pb;
+ u8_t *p;
+ u16_t len;
+
+ /* calculate UDP packet length */
+ len = 12 +8 +8 +8;
+
+ /* allocate a buffer */
+ pb = pbuf_alloc(PBUF_TRANSPORT, len, PBUF_RAM);
+ if (pb == NULL) {
+ return ERR_MEM;
+ }
+ LWIP_ASSERT("pb->tot_len == pb->len", pb->tot_len == pb->len);
+
+ p = (u8_t*)pb->payload;
+ /* fill in pkt */
+ /* L2TP control header */
+ PUTSHORT(PPPOL2TP_HEADERFLAG_CONTROL_MANDATORY, p);
+ PUTSHORT(len, p); /* Length */
+ PUTSHORT(l2tp->source_tunnel_id, p); /* Tunnel Id */
+ PUTSHORT(0, p); /* Session Id */
+ PUTSHORT(ns, p); /* NS Sequence number - to peer */
+ PUTSHORT(l2tp->peer_ns+1, p); /* NR Sequence number - expected for peer */
+
+ /* AVP - Message type */
+ PUTSHORT(PPPOL2TP_AVPHEADERFLAG_MANDATORY + 8, p); /* Mandatory flag + len field */
+ PUTSHORT(0, p); /* Vendor ID */
+ PUTSHORT(PPPOL2TP_AVPTYPE_MESSAGE, p); /* Attribute type: Message Type */
+ PUTSHORT(PPPOL2TP_MESSAGETYPE_STOPCCN, p); /* Attribute value: Message type: StopCCN */
+
+ /* AVP - Assign tunnel ID */
+ PUTSHORT(PPPOL2TP_AVPHEADERFLAG_MANDATORY + 8, p); /* Mandatory flag + len field */
+ PUTSHORT(0, p); /* Vendor ID */
+ PUTSHORT(PPPOL2TP_AVPTYPE_TUNNELID, p); /* Attribute type: Tunnel ID */
+ PUTSHORT(l2tp->remote_tunnel_id, p); /* Attribute value: Tunnel ID */
+
+ /* AVP - Result code */
+ PUTSHORT(PPPOL2TP_AVPHEADERFLAG_MANDATORY + 8, p); /* Mandatory flag + len field */
+ PUTSHORT(0, p); /* Vendor ID */
+ PUTSHORT(PPPOL2TP_AVPTYPE_RESULTCODE, p); /* Attribute type: Result code */
+ PUTSHORT(PPPOL2TP_RESULTCODE, p); /* Attribute value: Result code */
+
+ return pppol2tp_udp_send(l2tp, pb);
+}
+
+static err_t pppol2tp_xmit(pppol2tp_pcb *l2tp, struct pbuf *pb) {
+ u8_t *p;
+
+ /* make room for L2TP header - should not fail */
+ if (pbuf_header(pb, (s16_t)PPPOL2TP_OUTPUT_DATA_HEADER_LEN) != 0) {
+ /* bail out */
+ PPPDEBUG(LOG_ERR, ("pppol2tp: pppol2tp_pcb: could not allocate room for L2TP header\n"));
+ LINK_STATS_INC(link.lenerr);
+ pbuf_free(pb);
+ return ERR_BUF;
+ }
+
+ p = (u8_t*)pb->payload;
+ PUTSHORT(PPPOL2TP_HEADERFLAG_DATA_MANDATORY, p);
+ PUTSHORT(l2tp->source_tunnel_id, p); /* Tunnel Id */
+ PUTSHORT(l2tp->source_session_id, p); /* Session Id */
+
+ return pppol2tp_udp_send(l2tp, pb);
+}
+
+static err_t pppol2tp_udp_send(pppol2tp_pcb *l2tp, struct pbuf *pb) {
+ err_t err;
+ if (l2tp->netif) {
+ err = udp_sendto_if(l2tp->udp, pb, &l2tp->remote_ip, l2tp->tunnel_port, l2tp->netif);
+ } else {
+ err = udp_sendto(l2tp->udp, pb, &l2tp->remote_ip, l2tp->tunnel_port);
+ }
+ pbuf_free(pb);
+ return err;
+}
+
+#endif /* PPP_SUPPORT && PPPOL2TP_SUPPORT */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/pppos.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/pppos.c
new file mode 100644
index 0000000..fb48df4
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/pppos.c
@@ -0,0 +1,875 @@
+/**
+ * @file
+ * Network Point to Point Protocol over Serial file.
+ *
+ */
+
+/*
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT && PPPOS_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#include <string.h>
+
+#include "lwip/arch.h"
+#include "lwip/err.h"
+#include "lwip/pbuf.h"
+#include "lwip/sys.h"
+#include "lwip/memp.h"
+#include "lwip/netif.h"
+#include "lwip/snmp.h"
+#include "lwip/priv/tcpip_priv.h"
+#include "lwip/api.h"
+#include "lwip/ip4.h" /* for ip4_input() */
+
+#include "netif/ppp/ppp_impl.h"
+#include "netif/ppp/pppos.h"
+#include "netif/ppp/vj.h"
+
+/* Memory pool */
+LWIP_MEMPOOL_DECLARE(PPPOS_PCB, MEMP_NUM_PPPOS_INTERFACES, sizeof(pppos_pcb), "PPPOS_PCB")
+
+/* callbacks called from PPP core */
+static err_t pppos_write(ppp_pcb *ppp, void *ctx, struct pbuf *p);
+static err_t pppos_netif_output(ppp_pcb *ppp, void *ctx, struct pbuf *pb, u16_t protocol);
+static void pppos_connect(ppp_pcb *ppp, void *ctx);
+#if PPP_SERVER
+static void pppos_listen(ppp_pcb *ppp, void *ctx);
+#endif /* PPP_SERVER */
+static void pppos_disconnect(ppp_pcb *ppp, void *ctx);
+static err_t pppos_destroy(ppp_pcb *ppp, void *ctx);
+static void pppos_send_config(ppp_pcb *ppp, void *ctx, u32_t accm, int pcomp, int accomp);
+static void pppos_recv_config(ppp_pcb *ppp, void *ctx, u32_t accm, int pcomp, int accomp);
+
+/* Prototypes for procedures local to this file. */
+#if PPP_INPROC_IRQ_SAFE
+static void pppos_input_callback(void *arg);
+#endif /* PPP_INPROC_IRQ_SAFE */
+static void pppos_input_free_current_packet(pppos_pcb *pppos);
+static void pppos_input_drop(pppos_pcb *pppos);
+static err_t pppos_output_append(pppos_pcb *pppos, err_t err, struct pbuf *nb, u8_t c, u8_t accm, u16_t *fcs);
+static err_t pppos_output_last(pppos_pcb *pppos, err_t err, struct pbuf *nb, u16_t *fcs);
+
+/* Callbacks structure for PPP core */
+static const struct link_callbacks pppos_callbacks = {
+ pppos_connect,
+#if PPP_SERVER
+ pppos_listen,
+#endif /* PPP_SERVER */
+ pppos_disconnect,
+ pppos_destroy,
+ pppos_write,
+ pppos_netif_output,
+ pppos_send_config,
+ pppos_recv_config
+};
+
+/* PPP's Asynchronous-Control-Character-Map. The mask array is used
+ * to select the specific bit for a character. */
+#define ESCAPE_P(accm, c) ((accm)[(c) >> 3] & 1 << (c & 0x07))
+
+#if PPP_FCS_TABLE
+/*
+ * FCS lookup table as calculated by genfcstab.
+ */
+static const u16_t 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
+};
+#define PPP_FCS(fcs, c) (((fcs) >> 8) ^ fcstab[((fcs) ^ (c)) & 0xff])
+#else /* PPP_FCS_TABLE */
+/* The HDLC polynomial: X**0 + X**5 + X**12 + X**16 (0x8408) */
+#define PPP_FCS_POLYNOMIAL 0x8408
+static u16_t
+ppp_get_fcs(u8_t byte)
+{
+ unsigned int octet;
+ int bit;
+ octet = byte;
+ for (bit = 8; bit-- > 0; ) {
+ octet = (octet & 0x01) ? ((octet >> 1) ^ PPP_FCS_POLYNOMIAL) : (octet >> 1);
+ }
+ return octet & 0xffff;
+}
+#define PPP_FCS(fcs, c) (((fcs) >> 8) ^ ppp_get_fcs(((fcs) ^ (c)) & 0xff))
+#endif /* PPP_FCS_TABLE */
+
+/*
+ * Values for FCS calculations.
+ */
+#define PPP_INITFCS 0xffff /* Initial FCS value */
+#define PPP_GOODFCS 0xf0b8 /* Good final FCS value */
+
+#if PPP_INPROC_IRQ_SAFE
+#define PPPOS_DECL_PROTECT(lev) SYS_ARCH_DECL_PROTECT(lev)
+#define PPPOS_PROTECT(lev) SYS_ARCH_PROTECT(lev)
+#define PPPOS_UNPROTECT(lev) SYS_ARCH_UNPROTECT(lev)
+#else
+#define PPPOS_DECL_PROTECT(lev)
+#define PPPOS_PROTECT(lev)
+#define PPPOS_UNPROTECT(lev)
+#endif /* PPP_INPROC_IRQ_SAFE */
+
+
+/*
+ * Create a new PPP connection using the given serial I/O device.
+ *
+ * Return 0 on success, an error code on failure.
+ */
+ppp_pcb *pppos_create(struct netif *pppif, pppos_output_cb_fn output_cb,
+ ppp_link_status_cb_fn link_status_cb, void *ctx_cb)
+{
+ pppos_pcb *pppos;
+ ppp_pcb *ppp;
+
+ pppos = (pppos_pcb *)LWIP_MEMPOOL_ALLOC(PPPOS_PCB);
+ if (pppos == NULL) {
+ return NULL;
+ }
+
+ ppp = ppp_new(pppif, &pppos_callbacks, pppos, link_status_cb, ctx_cb);
+ if (ppp == NULL) {
+ LWIP_MEMPOOL_FREE(PPPOS_PCB, pppos);
+ return NULL;
+ }
+
+ memset(pppos, 0, sizeof(pppos_pcb));
+ pppos->ppp = ppp;
+ pppos->output_cb = output_cb;
+ return ppp;
+}
+
+/* Called by PPP core */
+static err_t
+pppos_write(ppp_pcb *ppp, void *ctx, struct pbuf *p)
+{
+ pppos_pcb *pppos = (pppos_pcb *)ctx;
+ u8_t *s;
+ struct pbuf *nb;
+ u16_t n;
+ u16_t fcs_out;
+ err_t err;
+ LWIP_UNUSED_ARG(ppp);
+
+ /* Grab an output buffer. */
+ nb = pbuf_alloc(PBUF_RAW, 0, PBUF_POOL);
+ if (nb == NULL) {
+ PPPDEBUG(LOG_WARNING, ("pppos_write[%d]: alloc fail\n", ppp->netif->num));
+ LINK_STATS_INC(link.memerr);
+ LINK_STATS_INC(link.drop);
+ MIB2_STATS_NETIF_INC(ppp->netif, ifoutdiscards);
+ pbuf_free(p);
+ return ERR_MEM;
+ }
+
+ /* If the link has been idle, we'll send a fresh flag character to
+ * flush any noise. */
+ err = ERR_OK;
+ if ((sys_now() - pppos->last_xmit) >= PPP_MAXIDLEFLAG) {
+ err = pppos_output_append(pppos, err, nb, PPP_FLAG, 0, NULL);
+ }
+
+ /* Load output buffer. */
+ fcs_out = PPP_INITFCS;
+ s = (u8_t*)p->payload;
+ n = p->len;
+ while (n-- > 0) {
+ err = pppos_output_append(pppos, err, nb, *s++, 1, &fcs_out);
+ }
+
+ err = pppos_output_last(pppos, err, nb, &fcs_out);
+ if (err == ERR_OK) {
+ PPPDEBUG(LOG_INFO, ("pppos_write[%d]: len=%d\n", ppp->netif->num, p->len));
+ } else {
+ PPPDEBUG(LOG_WARNING, ("pppos_write[%d]: output failed len=%d\n", ppp->netif->num, p->len));
+ }
+ pbuf_free(p);
+ return err;
+}
+
+/* Called by PPP core */
+static err_t
+pppos_netif_output(ppp_pcb *ppp, void *ctx, struct pbuf *pb, u16_t protocol)
+{
+ pppos_pcb *pppos = (pppos_pcb *)ctx;
+ struct pbuf *nb, *p;
+ u16_t fcs_out;
+ err_t err;
+ LWIP_UNUSED_ARG(ppp);
+
+ /* Grab an output buffer. */
+ nb = pbuf_alloc(PBUF_RAW, 0, PBUF_POOL);
+ if (nb == NULL) {
+ PPPDEBUG(LOG_WARNING, ("pppos_netif_output[%d]: alloc fail\n", ppp->netif->num));
+ LINK_STATS_INC(link.memerr);
+ LINK_STATS_INC(link.drop);
+ MIB2_STATS_NETIF_INC(ppp->netif, ifoutdiscards);
+ return ERR_MEM;
+ }
+
+ /* If the link has been idle, we'll send a fresh flag character to
+ * flush any noise. */
+ err = ERR_OK;
+ if ((sys_now() - pppos->last_xmit) >= PPP_MAXIDLEFLAG) {
+ err = pppos_output_append(pppos, err, nb, PPP_FLAG, 0, NULL);
+ }
+
+ fcs_out = PPP_INITFCS;
+ if (!pppos->accomp) {
+ err = pppos_output_append(pppos, err, nb, PPP_ALLSTATIONS, 1, &fcs_out);
+ err = pppos_output_append(pppos, err, nb, PPP_UI, 1, &fcs_out);
+ }
+ if (!pppos->pcomp || protocol > 0xFF) {
+ err = pppos_output_append(pppos, err, nb, (protocol >> 8) & 0xFF, 1, &fcs_out);
+ }
+ err = pppos_output_append(pppos, err, nb, protocol & 0xFF, 1, &fcs_out);
+
+ /* Load packet. */
+ for(p = pb; p; p = p->next) {
+ u16_t n = p->len;
+ u8_t *s = (u8_t*)p->payload;
+
+ while (n-- > 0) {
+ err = pppos_output_append(pppos, err, nb, *s++, 1, &fcs_out);
+ }
+ }
+
+ err = pppos_output_last(pppos, err, nb, &fcs_out);
+ if (err == ERR_OK) {
+ PPPDEBUG(LOG_INFO, ("pppos_netif_output[%d]: proto=0x%"X16_F", len = %d\n", ppp->netif->num, protocol, pb->tot_len));
+ } else {
+ PPPDEBUG(LOG_WARNING, ("pppos_netif_output[%d]: output failed proto=0x%"X16_F", len = %d\n", ppp->netif->num, protocol, pb->tot_len));
+ }
+ return err;
+}
+
+static void
+pppos_connect(ppp_pcb *ppp, void *ctx)
+{
+ pppos_pcb *pppos = (pppos_pcb *)ctx;
+ PPPOS_DECL_PROTECT(lev);
+
+#if PPP_INPROC_IRQ_SAFE
+ /* input pbuf left over from last session? */
+ pppos_input_free_current_packet(pppos);
+#endif /* PPP_INPROC_IRQ_SAFE */
+
+ /* reset PPPoS control block to its initial state */
+ memset(&pppos->last_xmit, 0, sizeof(pppos_pcb) - offsetof(pppos_pcb, last_xmit));
+
+ /*
+ * Default the in and out accm so that escape and flag characters
+ * are always escaped.
+ */
+ pppos->in_accm[15] = 0x60; /* no need to protect since RX is not running */
+ pppos->out_accm[15] = 0x60;
+ PPPOS_PROTECT(lev);
+ pppos->open = 1;
+ PPPOS_UNPROTECT(lev);
+
+ /*
+ * Start the connection and handle incoming events (packet or timeout).
+ */
+ PPPDEBUG(LOG_INFO, ("pppos_connect: unit %d: connecting\n", ppp->netif->num));
+ ppp_start(ppp); /* notify upper layers */
+}
+
+#if PPP_SERVER
+static void
+pppos_listen(ppp_pcb *ppp, void *ctx)
+{
+ pppos_pcb *pppos = (pppos_pcb *)ctx;
+ PPPOS_DECL_PROTECT(lev);
+
+#if PPP_INPROC_IRQ_SAFE
+ /* input pbuf left over from last session? */
+ pppos_input_free_current_packet(pppos);
+#endif /* PPP_INPROC_IRQ_SAFE */
+
+ /* reset PPPoS control block to its initial state */
+ memset(&pppos->last_xmit, 0, sizeof(pppos_pcb) - offsetof(pppos_pcb, last_xmit));
+
+ /*
+ * Default the in and out accm so that escape and flag characters
+ * are always escaped.
+ */
+ pppos->in_accm[15] = 0x60; /* no need to protect since RX is not running */
+ pppos->out_accm[15] = 0x60;
+ PPPOS_PROTECT(lev);
+ pppos->open = 1;
+ PPPOS_UNPROTECT(lev);
+
+ /*
+ * Wait for something to happen.
+ */
+ PPPDEBUG(LOG_INFO, ("pppos_listen: unit %d: listening\n", ppp->netif->num));
+ ppp_start(ppp); /* notify upper layers */
+}
+#endif /* PPP_SERVER */
+
+static void
+pppos_disconnect(ppp_pcb *ppp, void *ctx)
+{
+ pppos_pcb *pppos = (pppos_pcb *)ctx;
+ PPPOS_DECL_PROTECT(lev);
+
+ PPPOS_PROTECT(lev);
+ pppos->open = 0;
+ PPPOS_UNPROTECT(lev);
+
+ /* If PPP_INPROC_IRQ_SAFE is used we cannot call
+ * pppos_input_free_current_packet() here because
+ * rx IRQ might still call pppos_input().
+ */
+#if !PPP_INPROC_IRQ_SAFE
+ /* input pbuf left ? */
+ pppos_input_free_current_packet(pppos);
+#endif /* !PPP_INPROC_IRQ_SAFE */
+
+ ppp_link_end(ppp); /* notify upper layers */
+}
+
+static err_t
+pppos_destroy(ppp_pcb *ppp, void *ctx)
+{
+ pppos_pcb *pppos = (pppos_pcb *)ctx;
+ LWIP_UNUSED_ARG(ppp);
+
+#if PPP_INPROC_IRQ_SAFE
+ /* input pbuf left ? */
+ pppos_input_free_current_packet(pppos);
+#endif /* PPP_INPROC_IRQ_SAFE */
+
+ LWIP_MEMPOOL_FREE(PPPOS_PCB, pppos);
+ return ERR_OK;
+}
+
+#if !NO_SYS && !PPP_INPROC_IRQ_SAFE
+/** Pass received raw characters to PPPoS to be decoded through lwIP TCPIP thread.
+ *
+ * @param ppp PPP descriptor index, returned by pppos_create()
+ * @param s received data
+ * @param l length of received data
+ */
+err_t
+pppos_input_tcpip(ppp_pcb *ppp, u8_t *s, int l)
+{
+ struct pbuf *p;
+ err_t err;
+
+ p = pbuf_alloc(PBUF_RAW, l, PBUF_POOL);
+ if (!p) {
+ return ERR_MEM;
+ }
+ pbuf_take(p, s, l);
+
+ err = tcpip_inpkt(p, ppp_netif(ppp), pppos_input_sys);
+ if (err != ERR_OK) {
+ pbuf_free(p);
+ }
+ return err;
+}
+
+/* called from TCPIP thread */
+err_t pppos_input_sys(struct pbuf *p, struct netif *inp) {
+ ppp_pcb *ppp = (ppp_pcb*)inp->state;
+ struct pbuf *n;
+
+ for (n = p; n; n = n->next) {
+ pppos_input(ppp, (u8_t*)n->payload, n->len);
+ }
+ pbuf_free(p);
+ return ERR_OK;
+}
+#endif /* !NO_SYS && !PPP_INPROC_IRQ_SAFE */
+
+/** PPPoS input helper struct, must be packed since it is stored
+ * to pbuf->payload, which might be unaligned. */
+#if PPP_INPROC_IRQ_SAFE
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct pppos_input_header {
+ PACK_STRUCT_FIELD(ppp_pcb *ppp);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+#endif /* PPP_INPROC_IRQ_SAFE */
+
+/** Pass received raw characters to PPPoS to be decoded.
+ *
+ * @param ppp PPP descriptor index, returned by pppos_create()
+ * @param s received data
+ * @param l length of received data
+ */
+void
+pppos_input(ppp_pcb *ppp, u8_t *s, int l)
+{
+ pppos_pcb *pppos = (pppos_pcb *)ppp->link_ctx_cb;
+ struct pbuf *next_pbuf;
+ u8_t cur_char;
+ u8_t escaped;
+ PPPOS_DECL_PROTECT(lev);
+
+ PPPDEBUG(LOG_DEBUG, ("pppos_input[%d]: got %d bytes\n", ppp->netif->num, l));
+ while (l-- > 0) {
+ cur_char = *s++;
+
+ PPPOS_PROTECT(lev);
+ /* ppp_input can disconnect the interface, we need to abort to prevent a memory
+ * leak if there are remaining bytes because pppos_connect and pppos_listen
+ * functions expect input buffer to be free. Furthermore there are no real
+ * reason to continue reading bytes if we are disconnected.
+ */
+ if (!pppos->open) {
+ PPPOS_UNPROTECT(lev);
+ return;
+ }
+ escaped = ESCAPE_P(pppos->in_accm, cur_char);
+ PPPOS_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 (cur_char == PPP_ESCAPE) {
+ pppos->in_escaped = 1;
+ /* Check for the flag character. */
+ } else if (cur_char == PPP_FLAG) {
+ /* If this is just an extra flag character, ignore it. */
+ if (pppos->in_state <= PDADDRESS) {
+ /* ignore it */;
+ /* If we haven't received the packet header, drop what has come in. */
+ } else if (pppos->in_state < PDDATA) {
+ PPPDEBUG(LOG_WARNING,
+ ("pppos_input[%d]: Dropping incomplete packet %d\n",
+ ppp->netif->num, pppos->in_state));
+ LINK_STATS_INC(link.lenerr);
+ pppos_input_drop(pppos);
+ /* If the fcs is invalid, drop the packet. */
+ } else if (pppos->in_fcs != PPP_GOODFCS) {
+ PPPDEBUG(LOG_INFO,
+ ("pppos_input[%d]: Dropping bad fcs 0x%"X16_F" proto=0x%"X16_F"\n",
+ ppp->netif->num, pppos->in_fcs, pppos->in_protocol));
+ /* Note: If you get lots of these, check for UART frame errors or try different baud rate */
+ LINK_STATS_INC(link.chkerr);
+ pppos_input_drop(pppos);
+ /* Otherwise it's a good packet so pass it on. */
+ } else {
+ struct pbuf *inp;
+ /* Trim off the checksum. */
+ if(pppos->in_tail->len > 2) {
+ pppos->in_tail->len -= 2;
+
+ pppos->in_tail->tot_len = pppos->in_tail->len;
+ if (pppos->in_tail != pppos->in_head) {
+ pbuf_cat(pppos->in_head, pppos->in_tail);
+ }
+ } else {
+ pppos->in_tail->tot_len = pppos->in_tail->len;
+ if (pppos->in_tail != pppos->in_head) {
+ pbuf_cat(pppos->in_head, pppos->in_tail);
+ }
+
+ pbuf_realloc(pppos->in_head, pppos->in_head->tot_len - 2);
+ }
+
+ /* Dispatch the packet thereby consuming it. */
+ inp = pppos->in_head;
+ /* Packet consumed, release our references. */
+ pppos->in_head = NULL;
+ pppos->in_tail = NULL;
+#if IP_FORWARD || LWIP_IPV6_FORWARD
+ /* hide the room for Ethernet forwarding header */
+ pbuf_header(inp, -(s16_t)(PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN));
+#endif /* IP_FORWARD || LWIP_IPV6_FORWARD */
+#if PPP_INPROC_IRQ_SAFE
+ if(tcpip_callback_with_block(pppos_input_callback, inp, 0) != ERR_OK) {
+ PPPDEBUG(LOG_ERR, ("pppos_input[%d]: tcpip_callback() failed, dropping packet\n", ppp->netif->num));
+ pbuf_free(inp);
+ LINK_STATS_INC(link.drop);
+ MIB2_STATS_NETIF_INC(ppp->netif, ifindiscards);
+ }
+#else /* PPP_INPROC_IRQ_SAFE */
+ ppp_input(ppp, inp);
+#endif /* PPP_INPROC_IRQ_SAFE */
+ }
+
+ /* Prepare for a new packet. */
+ pppos->in_fcs = PPP_INITFCS;
+ pppos->in_state = PDADDRESS;
+ pppos->in_escaped = 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,
+ ("pppos_input[%d]: Dropping ACCM char <%d>\n", ppp->netif->num, cur_char));
+ }
+ /* Process other characters. */
+ } else {
+ /* Unencode escaped characters. */
+ if (pppos->in_escaped) {
+ pppos->in_escaped = 0;
+ cur_char ^= PPP_TRANS;
+ }
+
+ /* Process character relative to current state. */
+ switch(pppos->in_state) {
+ case PDIDLE: /* Idle state - waiting. */
+ /* Drop the character if it's not 0xff
+ * we would have processed a flag character above. */
+ if (cur_char != PPP_ALLSTATIONS) {
+ break;
+ }
+ /* no break */
+ /* Fall through */
+
+ case PDSTART: /* Process start flag. */
+ /* Prepare for a new packet. */
+ pppos->in_fcs = PPP_INITFCS;
+ /* no break */
+ /* Fall through */
+
+ case PDADDRESS: /* Process address field. */
+ if (cur_char == PPP_ALLSTATIONS) {
+ pppos->in_state = PDCONTROL;
+ break;
+ }
+ /* no 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 (cur_char == PPP_UI) {
+ pppos->in_state = PDPROTOCOL1;
+ break;
+ }
+ /* no break */
+
+#if 0
+ else {
+ PPPDEBUG(LOG_WARNING,
+ ("pppos_input[%d]: Invalid control <%d>\n", ppp->netif->num, cur_char));
+ pppos->in_state = PDSTART;
+ }
+#endif
+ case PDPROTOCOL1: /* Process protocol field 1. */
+ /* If the lower bit is set, this is the end of the protocol
+ * field. */
+ if (cur_char & 1) {
+ pppos->in_protocol = cur_char;
+ pppos->in_state = PDDATA;
+ } else {
+ pppos->in_protocol = (u16_t)cur_char << 8;
+ pppos->in_state = PDPROTOCOL2;
+ }
+ break;
+ case PDPROTOCOL2: /* Process protocol field 2. */
+ pppos->in_protocol |= cur_char;
+ pppos->in_state = PDDATA;
+ break;
+ case PDDATA: /* Process data byte. */
+ /* Make space to receive processed data. */
+ if (pppos->in_tail == NULL || pppos->in_tail->len == PBUF_POOL_BUFSIZE) {
+ u16_t pbuf_alloc_len;
+ if (pppos->in_tail != NULL) {
+ pppos->in_tail->tot_len = pppos->in_tail->len;
+ if (pppos->in_tail != pppos->in_head) {
+ pbuf_cat(pppos->in_head, pppos->in_tail);
+ /* give up the in_tail reference now */
+ pppos->in_tail = NULL;
+ }
+ }
+ /* If we haven't started a packet, we need a packet header. */
+ pbuf_alloc_len = 0;
+#if IP_FORWARD || LWIP_IPV6_FORWARD
+ /* If IP forwarding is enabled we are reserving PBUF_LINK_ENCAPSULATION_HLEN
+ * + PBUF_LINK_HLEN bytes so the packet is being allocated with enough header
+ * space to be forwarded (to Ethernet for example).
+ */
+ if (pppos->in_head == NULL) {
+ pbuf_alloc_len = PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN;
+ }
+#endif /* IP_FORWARD || LWIP_IPV6_FORWARD */
+ next_pbuf = pbuf_alloc(PBUF_RAW, pbuf_alloc_len, PBUF_POOL);
+ if (next_pbuf == 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, ("pppos_input[%d]: NO FREE PBUFS!\n", ppp->netif->num));
+ LINK_STATS_INC(link.memerr);
+ pppos_input_drop(pppos);
+ pppos->in_state = PDSTART; /* Wait for flag sequence. */
+ break;
+ }
+ if (pppos->in_head == NULL) {
+ u8_t *payload = ((u8_t*)next_pbuf->payload) + pbuf_alloc_len;
+#if PPP_INPROC_IRQ_SAFE
+ ((struct pppos_input_header*)payload)->ppp = ppp;
+ payload += sizeof(struct pppos_input_header);
+ next_pbuf->len += sizeof(struct pppos_input_header);
+#endif /* PPP_INPROC_IRQ_SAFE */
+ next_pbuf->len += sizeof(pppos->in_protocol);
+ *(payload++) = pppos->in_protocol >> 8;
+ *(payload) = pppos->in_protocol & 0xFF;
+ pppos->in_head = next_pbuf;
+ }
+ pppos->in_tail = next_pbuf;
+ }
+ /* Load character into buffer. */
+ ((u8_t*)pppos->in_tail->payload)[pppos->in_tail->len++] = cur_char;
+ break;
+ default:
+ break;
+ }
+
+ /* update the frame check sequence number. */
+ pppos->in_fcs = PPP_FCS(pppos->in_fcs, cur_char);
+ }
+ } /* while (l-- > 0), all bytes processed */
+}
+
+#if PPP_INPROC_IRQ_SAFE
+/* PPPoS input callback using one input pointer
+ */
+static void pppos_input_callback(void *arg) {
+ struct pbuf *pb = (struct pbuf*)arg;
+ ppp_pcb *ppp;
+
+ ppp = ((struct pppos_input_header*)pb->payload)->ppp;
+ if(pbuf_header(pb, -(s16_t)sizeof(struct pppos_input_header))) {
+ LWIP_ASSERT("pbuf_header failed\n", 0);
+ goto drop;
+ }
+
+ /* Dispatch the packet thereby consuming it. */
+ ppp_input(ppp, pb);
+ return;
+
+drop:
+ LINK_STATS_INC(link.drop);
+ MIB2_STATS_NETIF_INC(ppp->netif, ifindiscards);
+ pbuf_free(pb);
+}
+#endif /* PPP_INPROC_IRQ_SAFE */
+
+static void
+pppos_send_config(ppp_pcb *ppp, void *ctx, u32_t accm, int pcomp, int accomp)
+{
+ int i;
+ pppos_pcb *pppos = (pppos_pcb *)ctx;
+ LWIP_UNUSED_ARG(ppp);
+
+ pppos->pcomp = pcomp;
+ pppos->accomp = accomp;
+
+ /* Load the ACCM bits for the 32 control codes. */
+ for (i = 0; i < 32/8; i++) {
+ pppos->out_accm[i] = (u8_t)((accm >> (8 * i)) & 0xFF);
+ }
+
+ PPPDEBUG(LOG_INFO, ("pppos_send_config[%d]: out_accm=%X %X %X %X\n",
+ pppos->ppp->netif->num,
+ pppos->out_accm[0], pppos->out_accm[1], pppos->out_accm[2], pppos->out_accm[3]));
+}
+
+static void
+pppos_recv_config(ppp_pcb *ppp, void *ctx, u32_t accm, int pcomp, int accomp)
+{
+ int i;
+ pppos_pcb *pppos = (pppos_pcb *)ctx;
+ PPPOS_DECL_PROTECT(lev);
+ LWIP_UNUSED_ARG(ppp);
+ LWIP_UNUSED_ARG(pcomp);
+ LWIP_UNUSED_ARG(accomp);
+
+ /* Load the ACCM bits for the 32 control codes. */
+ PPPOS_PROTECT(lev);
+ for (i = 0; i < 32 / 8; i++) {
+ pppos->in_accm[i] = (u8_t)(accm >> (i * 8));
+ }
+ PPPOS_UNPROTECT(lev);
+
+ PPPDEBUG(LOG_INFO, ("pppos_recv_config[%d]: in_accm=%X %X %X %X\n",
+ pppos->ppp->netif->num,
+ pppos->in_accm[0], pppos->in_accm[1], pppos->in_accm[2], pppos->in_accm[3]));
+}
+
+/*
+ * Drop the input packet.
+ */
+static void
+pppos_input_free_current_packet(pppos_pcb *pppos)
+{
+ if (pppos->in_head != NULL) {
+ if (pppos->in_tail && (pppos->in_tail != pppos->in_head)) {
+ pbuf_free(pppos->in_tail);
+ }
+ pbuf_free(pppos->in_head);
+ pppos->in_head = NULL;
+ }
+ pppos->in_tail = NULL;
+}
+
+/*
+ * Drop the input packet and increase error counters.
+ */
+static void
+pppos_input_drop(pppos_pcb *pppos)
+{
+ if (pppos->in_head != NULL) {
+#if 0
+ PPPDEBUG(LOG_INFO, ("pppos_input_drop: %d:%.*H\n", pppos->in_head->len, min(60, pppos->in_head->len * 2), pppos->in_head->payload));
+#endif
+ PPPDEBUG(LOG_INFO, ("pppos_input_drop: pbuf len=%d, addr %p\n", pppos->in_head->len, (void*)pppos->in_head));
+ }
+ pppos_input_free_current_packet(pppos);
+#if VJ_SUPPORT
+ vj_uncompress_err(&pppos->ppp->vj_comp);
+#endif /* VJ_SUPPORT */
+
+ LINK_STATS_INC(link.drop);
+ MIB2_STATS_NETIF_INC(pppos->ppp->netif, ifindiscards);
+}
+
+/*
+ * pppos_output_append - append given character to end of given pbuf.
+ * If out_accm is not 0 and the character needs to be escaped, do so.
+ * If pbuf is full, send the pbuf and reuse it.
+ * Return the current pbuf.
+ */
+static err_t
+pppos_output_append(pppos_pcb *pppos, err_t err, struct pbuf *nb, u8_t c, u8_t accm, u16_t *fcs)
+{
+ if (err != ERR_OK) {
+ return err;
+ }
+
+ /* 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? */
+ if ((PBUF_POOL_BUFSIZE - nb->len) < 2) {
+ u32_t l = pppos->output_cb(pppos->ppp, (u8_t*)nb->payload, nb->len, pppos->ppp->ctx_cb);
+ if (l != nb->len) {
+ return ERR_IF;
+ }
+ nb->len = 0;
+ }
+
+ /* Update FCS before checking for special characters. */
+ if (fcs) {
+ *fcs = PPP_FCS(*fcs, c);
+ }
+
+ /* Copy to output buffer escaping special characters. */
+ if (accm && ESCAPE_P(pppos->out_accm, c)) {
+ *((u8_t*)nb->payload + nb->len++) = PPP_ESCAPE;
+ *((u8_t*)nb->payload + nb->len++) = c ^ PPP_TRANS;
+ } else {
+ *((u8_t*)nb->payload + nb->len++) = c;
+ }
+
+ return ERR_OK;
+}
+
+static err_t
+pppos_output_last(pppos_pcb *pppos, err_t err, struct pbuf *nb, u16_t *fcs)
+{
+ ppp_pcb *ppp = pppos->ppp;
+
+ /* Add FCS and trailing flag. */
+ err = pppos_output_append(pppos, err, nb, ~(*fcs) & 0xFF, 1, NULL);
+ err = pppos_output_append(pppos, err, nb, (~(*fcs) >> 8) & 0xFF, 1, NULL);
+ err = pppos_output_append(pppos, err, nb, PPP_FLAG, 0, NULL);
+
+ if (err != ERR_OK) {
+ goto failed;
+ }
+
+ /* Send remaining buffer if not empty */
+ if (nb->len > 0) {
+ u32_t l = pppos->output_cb(ppp, (u8_t*)nb->payload, nb->len, ppp->ctx_cb);
+ if (l != nb->len) {
+ err = ERR_IF;
+ goto failed;
+ }
+ }
+
+ pppos->last_xmit = sys_now();
+ MIB2_STATS_NETIF_ADD(ppp->netif, ifoutoctets, nb->tot_len);
+ MIB2_STATS_NETIF_INC(ppp->netif, ifoutucastpkts);
+ LINK_STATS_INC(link.xmit);
+ pbuf_free(nb);
+ return ERR_OK;
+
+failed:
+ pppos->last_xmit = 0; /* prepend PPP_FLAG to next packet */
+ LINK_STATS_INC(link.err);
+ LINK_STATS_INC(link.drop);
+ MIB2_STATS_NETIF_INC(ppp->netif, ifoutdiscards);
+ pbuf_free(nb);
+ return err;
+}
+
+#endif /* PPP_SUPPORT && PPPOS_SUPPORT */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/upap.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/upap.c
new file mode 100644
index 0000000..d00c2d7
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/upap.c
@@ -0,0 +1,677 @@
+/*
+ * upap.c - User/Password Authentication Protocol.
+ *
+ * Copyright (c) 1984-2000 Carnegie Mellon University. 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 "Carnegie Mellon University" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission. For permission or any legal
+ * details, please contact
+ * Office of Technology Transfer
+ * Carnegie Mellon University
+ * 5000 Forbes Avenue
+ * Pittsburgh, PA 15213-3890
+ * (412) 268-4387, fax: (412) 268-7395
+ * tech-transfer@andrew.cmu.edu
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by Computing Services
+ * at Carnegie Mellon University (http://www.cmu.edu/computing/)."
+ *
+ * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
+ * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT && PAP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+/*
+ * @todo:
+ */
+
+#if 0 /* UNUSED */
+#include <stdio.h>
+#include <string.h>
+#endif /* UNUSED */
+
+#include "netif/ppp/ppp_impl.h"
+
+#include "netif/ppp/upap.h"
+
+#if PPP_OPTIONS
+/*
+ * Command-line options.
+ */
+static option_t pap_option_list[] = {
+ { "hide-password", o_bool, &hide_password,
+ "Don't output passwords to log", OPT_PRIO | 1 },
+ { "show-password", o_bool, &hide_password,
+ "Show password string in debug log messages", OPT_PRIOSUB | 0 },
+
+ { "pap-restart", o_int, &upap[0].us_timeouttime,
+ "Set retransmit timeout for PAP", OPT_PRIO },
+ { "pap-max-authreq", o_int, &upap[0].us_maxtransmits,
+ "Set max number of transmissions for auth-reqs", OPT_PRIO },
+ { "pap-timeout", o_int, &upap[0].us_reqtimeout,
+ "Set time limit for peer PAP authentication", OPT_PRIO },
+
+ { NULL }
+};
+#endif /* PPP_OPTIONS */
+
+/*
+ * Protocol entry points.
+ */
+static void upap_init(ppp_pcb *pcb);
+static void upap_lowerup(ppp_pcb *pcb);
+static void upap_lowerdown(ppp_pcb *pcb);
+static void upap_input(ppp_pcb *pcb, u_char *inpacket, int l);
+static void upap_protrej(ppp_pcb *pcb);
+#if PRINTPKT_SUPPORT
+static int upap_printpkt(const u_char *p, int plen, void (*printer) (void *, const char *, ...), void *arg);
+#endif /* PRINTPKT_SUPPORT */
+
+const struct protent pap_protent = {
+ PPP_PAP,
+ upap_init,
+ upap_input,
+ upap_protrej,
+ upap_lowerup,
+ upap_lowerdown,
+ NULL,
+ NULL,
+#if PRINTPKT_SUPPORT
+ upap_printpkt,
+#endif /* PRINTPKT_SUPPORT */
+#if PPP_DATAINPUT
+ NULL,
+#endif /* PPP_DATAINPUT */
+#if PRINTPKT_SUPPORT
+ "PAP",
+ NULL,
+#endif /* PRINTPKT_SUPPORT */
+#if PPP_OPTIONS
+ pap_option_list,
+ NULL,
+#endif /* PPP_OPTIONS */
+#if DEMAND_SUPPORT
+ NULL,
+ NULL
+#endif /* DEMAND_SUPPORT */
+};
+
+static void upap_timeout(void *arg);
+#if PPP_SERVER
+static void upap_reqtimeout(void *arg);
+static void upap_rauthreq(ppp_pcb *pcb, u_char *inp, int id, int len);
+#endif /* PPP_SERVER */
+static void upap_rauthack(ppp_pcb *pcb, u_char *inp, int id, int len);
+static void upap_rauthnak(ppp_pcb *pcb, u_char *inp, int id, int len);
+static void upap_sauthreq(ppp_pcb *pcb);
+#if PPP_SERVER
+static void upap_sresp(ppp_pcb *pcb, u_char code, u_char id, const char *msg, int msglen);
+#endif /* PPP_SERVER */
+
+
+/*
+ * upap_init - Initialize a UPAP unit.
+ */
+static void upap_init(ppp_pcb *pcb) {
+ pcb->upap.us_user = NULL;
+ pcb->upap.us_userlen = 0;
+ pcb->upap.us_passwd = NULL;
+ pcb->upap.us_passwdlen = 0;
+ pcb->upap.us_clientstate = UPAPCS_INITIAL;
+#if PPP_SERVER
+ pcb->upap.us_serverstate = UPAPSS_INITIAL;
+#endif /* PPP_SERVER */
+ pcb->upap.us_id = 0;
+}
+
+
+/*
+ * upap_authwithpeer - Authenticate us with our peer (start client).
+ *
+ * Set new state and send authenticate's.
+ */
+void upap_authwithpeer(ppp_pcb *pcb, const char *user, const char *password) {
+
+ if(!user || !password)
+ return;
+
+ /* Save the username and password we're given */
+ pcb->upap.us_user = user;
+ pcb->upap.us_userlen = LWIP_MIN(strlen(user), 0xff);
+ pcb->upap.us_passwd = password;
+ pcb->upap.us_passwdlen = LWIP_MIN(strlen(password), 0xff);
+ pcb->upap.us_transmits = 0;
+
+ /* Lower layer up yet? */
+ if (pcb->upap.us_clientstate == UPAPCS_INITIAL ||
+ pcb->upap.us_clientstate == UPAPCS_PENDING) {
+ pcb->upap.us_clientstate = UPAPCS_PENDING;
+ return;
+ }
+
+ upap_sauthreq(pcb); /* Start protocol */
+}
+
+#if PPP_SERVER
+/*
+ * upap_authpeer - Authenticate our peer (start server).
+ *
+ * Set new state.
+ */
+void upap_authpeer(ppp_pcb *pcb) {
+
+ /* Lower layer up yet? */
+ if (pcb->upap.us_serverstate == UPAPSS_INITIAL ||
+ pcb->upap.us_serverstate == UPAPSS_PENDING) {
+ pcb->upap.us_serverstate = UPAPSS_PENDING;
+ return;
+ }
+
+ pcb->upap.us_serverstate = UPAPSS_LISTEN;
+ if (pcb->settings.pap_req_timeout > 0)
+ TIMEOUT(upap_reqtimeout, pcb, pcb->settings.pap_req_timeout);
+}
+#endif /* PPP_SERVER */
+
+/*
+ * upap_timeout - Retransmission timer for sending auth-reqs expired.
+ */
+static void upap_timeout(void *arg) {
+ ppp_pcb *pcb = (ppp_pcb*)arg;
+
+ if (pcb->upap.us_clientstate != UPAPCS_AUTHREQ)
+ return;
+
+ if (pcb->upap.us_transmits >= pcb->settings.pap_max_transmits) {
+ /* give up in disgust */
+ ppp_error("No response to PAP authenticate-requests");
+ pcb->upap.us_clientstate = UPAPCS_BADAUTH;
+ auth_withpeer_fail(pcb, PPP_PAP);
+ return;
+ }
+
+ upap_sauthreq(pcb); /* Send Authenticate-Request */
+}
+
+
+#if PPP_SERVER
+/*
+ * upap_reqtimeout - Give up waiting for the peer to send an auth-req.
+ */
+static void upap_reqtimeout(void *arg) {
+ ppp_pcb *pcb = (ppp_pcb*)arg;
+
+ if (pcb->upap.us_serverstate != UPAPSS_LISTEN)
+ return; /* huh?? */
+
+ auth_peer_fail(pcb, PPP_PAP);
+ pcb->upap.us_serverstate = UPAPSS_BADAUTH;
+}
+#endif /* PPP_SERVER */
+
+
+/*
+ * upap_lowerup - The lower layer is up.
+ *
+ * Start authenticating if pending.
+ */
+static void upap_lowerup(ppp_pcb *pcb) {
+
+ if (pcb->upap.us_clientstate == UPAPCS_INITIAL)
+ pcb->upap.us_clientstate = UPAPCS_CLOSED;
+ else if (pcb->upap.us_clientstate == UPAPCS_PENDING) {
+ upap_sauthreq(pcb); /* send an auth-request */
+ }
+
+#if PPP_SERVER
+ if (pcb->upap.us_serverstate == UPAPSS_INITIAL)
+ pcb->upap.us_serverstate = UPAPSS_CLOSED;
+ else if (pcb->upap.us_serverstate == UPAPSS_PENDING) {
+ pcb->upap.us_serverstate = UPAPSS_LISTEN;
+ if (pcb->settings.pap_req_timeout > 0)
+ TIMEOUT(upap_reqtimeout, pcb, pcb->settings.pap_req_timeout);
+ }
+#endif /* PPP_SERVER */
+}
+
+
+/*
+ * upap_lowerdown - The lower layer is down.
+ *
+ * Cancel all timeouts.
+ */
+static void upap_lowerdown(ppp_pcb *pcb) {
+
+ if (pcb->upap.us_clientstate == UPAPCS_AUTHREQ) /* Timeout pending? */
+ UNTIMEOUT(upap_timeout, pcb); /* Cancel timeout */
+#if PPP_SERVER
+ if (pcb->upap.us_serverstate == UPAPSS_LISTEN && pcb->settings.pap_req_timeout > 0)
+ UNTIMEOUT(upap_reqtimeout, pcb);
+#endif /* PPP_SERVER */
+
+ pcb->upap.us_clientstate = UPAPCS_INITIAL;
+#if PPP_SERVER
+ pcb->upap.us_serverstate = UPAPSS_INITIAL;
+#endif /* PPP_SERVER */
+}
+
+
+/*
+ * upap_protrej - Peer doesn't speak this protocol.
+ *
+ * This shouldn't happen. In any case, pretend lower layer went down.
+ */
+static void upap_protrej(ppp_pcb *pcb) {
+
+ if (pcb->upap.us_clientstate == UPAPCS_AUTHREQ) {
+ ppp_error("PAP authentication failed due to protocol-reject");
+ auth_withpeer_fail(pcb, PPP_PAP);
+ }
+#if PPP_SERVER
+ if (pcb->upap.us_serverstate == UPAPSS_LISTEN) {
+ ppp_error("PAP authentication of peer failed (protocol-reject)");
+ auth_peer_fail(pcb, PPP_PAP);
+ }
+#endif /* PPP_SERVER */
+ upap_lowerdown(pcb);
+}
+
+
+/*
+ * upap_input - Input UPAP packet.
+ */
+static void upap_input(ppp_pcb *pcb, u_char *inpacket, int l) {
+ 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 < UPAP_HEADERLEN) {
+ UPAPDEBUG(("pap_input: rcvd short header."));
+ return;
+ }
+ GETCHAR(code, inp);
+ GETCHAR(id, inp);
+ GETSHORT(len, inp);
+ if (len < UPAP_HEADERLEN) {
+ UPAPDEBUG(("pap_input: rcvd illegal length."));
+ return;
+ }
+ if (len > l) {
+ UPAPDEBUG(("pap_input: rcvd short packet."));
+ return;
+ }
+ len -= UPAP_HEADERLEN;
+
+ /*
+ * Action depends on code.
+ */
+ switch (code) {
+ case UPAP_AUTHREQ:
+#if PPP_SERVER
+ upap_rauthreq(pcb, inp, id, len);
+#endif /* PPP_SERVER */
+ break;
+
+ case UPAP_AUTHACK:
+ upap_rauthack(pcb, inp, id, len);
+ break;
+
+ case UPAP_AUTHNAK:
+ upap_rauthnak(pcb, inp, id, len);
+ break;
+
+ default: /* XXX Need code reject */
+ break;
+ }
+}
+
+#if PPP_SERVER
+/*
+ * upap_rauth - Receive Authenticate.
+ */
+static void upap_rauthreq(ppp_pcb *pcb, u_char *inp, int id, int len) {
+ u_char ruserlen, rpasswdlen;
+ char *ruser;
+ char *rpasswd;
+ char rhostname[256];
+ int retcode;
+ const char *msg;
+ int msglen;
+
+ if (pcb->upap.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 (pcb->upap.us_serverstate == UPAPSS_OPEN) {
+ upap_sresp(pcb, UPAP_AUTHACK, id, "", 0); /* return auth-ack */
+ return;
+ }
+ if (pcb->upap.us_serverstate == UPAPSS_BADAUTH) {
+ upap_sresp(pcb, UPAP_AUTHNAK, id, "", 0); /* return auth-nak */
+ return;
+ }
+
+ /*
+ * Parse user/passwd.
+ */
+ if (len < 1) {
+ UPAPDEBUG(("pap_rauth: rcvd short packet."));
+ return;
+ }
+ GETCHAR(ruserlen, inp);
+ len -= sizeof (u_char) + ruserlen + sizeof (u_char);
+ if (len < 0) {
+ UPAPDEBUG(("pap_rauth: rcvd short packet."));
+ return;
+ }
+ ruser = (char *) inp;
+ INCPTR(ruserlen, inp);
+ GETCHAR(rpasswdlen, inp);
+ if (len < rpasswdlen) {
+ UPAPDEBUG(("pap_rauth: rcvd short packet."));
+ return;
+ }
+
+ rpasswd = (char *) inp;
+
+ /*
+ * Check the username and password given.
+ */
+ retcode = UPAP_AUTHNAK;
+ if (auth_check_passwd(pcb, ruser, ruserlen, rpasswd, rpasswdlen, &msg, &msglen)) {
+ retcode = UPAP_AUTHACK;
+ }
+ BZERO(rpasswd, rpasswdlen);
+
+#if 0 /* UNUSED */
+ /*
+ * Check remote number authorization. A plugin may have filled in
+ * the remote number or added an allowed number, and rather than
+ * return an authenticate failure, is leaving it for us to verify.
+ */
+ if (retcode == UPAP_AUTHACK) {
+ if (!auth_number()) {
+ /* We do not want to leak info about the pap result. */
+ retcode = UPAP_AUTHNAK; /* XXX exit value will be "wrong" */
+ warn("calling number %q is not authorized", remote_number);
+ }
+ }
+
+ msglen = strlen(msg);
+ if (msglen > 255)
+ msglen = 255;
+#endif /* UNUSED */
+
+ upap_sresp(pcb, retcode, id, msg, msglen);
+
+ /* Null terminate and clean remote name. */
+ ppp_slprintf(rhostname, sizeof(rhostname), "%.*v", ruserlen, ruser);
+
+ if (retcode == UPAP_AUTHACK) {
+ pcb->upap.us_serverstate = UPAPSS_OPEN;
+ ppp_notice("PAP peer authentication succeeded for %q", rhostname);
+ auth_peer_success(pcb, PPP_PAP, 0, ruser, ruserlen);
+ } else {
+ pcb->upap.us_serverstate = UPAPSS_BADAUTH;
+ ppp_warn("PAP peer authentication failed for %q", rhostname);
+ auth_peer_fail(pcb, PPP_PAP);
+ }
+
+ if (pcb->settings.pap_req_timeout > 0)
+ UNTIMEOUT(upap_reqtimeout, pcb);
+}
+#endif /* PPP_SERVER */
+
+/*
+ * upap_rauthack - Receive Authenticate-Ack.
+ */
+static void upap_rauthack(ppp_pcb *pcb, u_char *inp, int id, int len) {
+ u_char msglen;
+ char *msg;
+ LWIP_UNUSED_ARG(id);
+
+ if (pcb->upap.us_clientstate != UPAPCS_AUTHREQ) /* XXX */
+ return;
+
+ /*
+ * Parse message.
+ */
+ if (len < 1) {
+ UPAPDEBUG(("pap_rauthack: ignoring missing msg-length."));
+ } else {
+ GETCHAR(msglen, inp);
+ if (msglen > 0) {
+ len -= sizeof (u_char);
+ if (len < msglen) {
+ UPAPDEBUG(("pap_rauthack: rcvd short packet."));
+ return;
+ }
+ msg = (char *) inp;
+ PRINTMSG(msg, msglen);
+ }
+ }
+
+ pcb->upap.us_clientstate = UPAPCS_OPEN;
+
+ auth_withpeer_success(pcb, PPP_PAP, 0);
+}
+
+
+/*
+ * upap_rauthnak - Receive Authenticate-Nak.
+ */
+static void upap_rauthnak(ppp_pcb *pcb, u_char *inp, int id, int len) {
+ u_char msglen;
+ char *msg;
+ LWIP_UNUSED_ARG(id);
+
+ if (pcb->upap.us_clientstate != UPAPCS_AUTHREQ) /* XXX */
+ return;
+
+ /*
+ * Parse message.
+ */
+ if (len < 1) {
+ UPAPDEBUG(("pap_rauthnak: ignoring missing msg-length."));
+ } else {
+ GETCHAR(msglen, inp);
+ if (msglen > 0) {
+ len -= sizeof (u_char);
+ if (len < msglen) {
+ UPAPDEBUG(("pap_rauthnak: rcvd short packet."));
+ return;
+ }
+ msg = (char *) inp;
+ PRINTMSG(msg, msglen);
+ }
+ }
+
+ pcb->upap.us_clientstate = UPAPCS_BADAUTH;
+
+ ppp_error("PAP authentication failed");
+ auth_withpeer_fail(pcb, PPP_PAP);
+}
+
+
+/*
+ * upap_sauthreq - Send an Authenticate-Request.
+ */
+static void upap_sauthreq(ppp_pcb *pcb) {
+ struct pbuf *p;
+ u_char *outp;
+ int outlen;
+
+ outlen = UPAP_HEADERLEN + 2 * sizeof (u_char) +
+ pcb->upap.us_userlen + pcb->upap.us_passwdlen;
+ p = pbuf_alloc(PBUF_RAW, (u16_t)(PPP_HDRLEN +outlen), PPP_CTRL_PBUF_TYPE);
+ if(NULL == p)
+ return;
+ if(p->tot_len != p->len) {
+ pbuf_free(p);
+ return;
+ }
+
+ outp = (u_char*)p->payload;
+ MAKEHEADER(outp, PPP_PAP);
+
+ PUTCHAR(UPAP_AUTHREQ, outp);
+ PUTCHAR(++pcb->upap.us_id, outp);
+ PUTSHORT(outlen, outp);
+ PUTCHAR(pcb->upap.us_userlen, outp);
+ MEMCPY(outp, pcb->upap.us_user, pcb->upap.us_userlen);
+ INCPTR(pcb->upap.us_userlen, outp);
+ PUTCHAR(pcb->upap.us_passwdlen, outp);
+ MEMCPY(outp, pcb->upap.us_passwd, pcb->upap.us_passwdlen);
+
+ ppp_write(pcb, p);
+
+ TIMEOUT(upap_timeout, pcb, pcb->settings.pap_timeout_time);
+ ++pcb->upap.us_transmits;
+ pcb->upap.us_clientstate = UPAPCS_AUTHREQ;
+}
+
+#if PPP_SERVER
+/*
+ * upap_sresp - Send a response (ack or nak).
+ */
+static void upap_sresp(ppp_pcb *pcb, u_char code, u_char id, const char *msg, int msglen) {
+ struct pbuf *p;
+ u_char *outp;
+ int outlen;
+
+ outlen = UPAP_HEADERLEN + sizeof (u_char) + msglen;
+ p = pbuf_alloc(PBUF_RAW, (u16_t)(PPP_HDRLEN +outlen), PPP_CTRL_PBUF_TYPE);
+ if(NULL == p)
+ return;
+ if(p->tot_len != p->len) {
+ pbuf_free(p);
+ return;
+ }
+
+ outp = (u_char*)p->payload;
+ MAKEHEADER(outp, PPP_PAP);
+
+ PUTCHAR(code, outp);
+ PUTCHAR(id, outp);
+ PUTSHORT(outlen, outp);
+ PUTCHAR(msglen, outp);
+ MEMCPY(outp, msg, msglen);
+
+ ppp_write(pcb, p);
+}
+#endif /* PPP_SERVER */
+
+#if PRINTPKT_SUPPORT
+/*
+ * upap_printpkt - print the contents of a PAP packet.
+ */
+static const char* const upap_codenames[] = {
+ "AuthReq", "AuthAck", "AuthNak"
+};
+
+static int upap_printpkt(const u_char *p, int plen, void (*printer) (void *, const char *, ...), void *arg) {
+ int code, id, len;
+ int mlen, ulen, wlen;
+ const u_char *user, *pwd, *msg;
+ const u_char *pstart;
+
+ if (plen < UPAP_HEADERLEN)
+ return 0;
+ pstart = p;
+ GETCHAR(code, p);
+ GETCHAR(id, p);
+ GETSHORT(len, p);
+ if (len < UPAP_HEADERLEN || len > plen)
+ return 0;
+
+ if (code >= 1 && code <= (int)LWIP_ARRAYSIZE(upap_codenames))
+ printer(arg, " %s", upap_codenames[code-1]);
+ else
+ printer(arg, " code=0x%x", code);
+ printer(arg, " id=0x%x", id);
+ len -= UPAP_HEADERLEN;
+ switch (code) {
+ case UPAP_AUTHREQ:
+ if (len < 1)
+ break;
+ ulen = p[0];
+ if (len < ulen + 2)
+ break;
+ wlen = p[ulen + 1];
+ if (len < ulen + wlen + 2)
+ break;
+ user = (const u_char *) (p + 1);
+ pwd = (const u_char *) (p + ulen + 2);
+ p += ulen + wlen + 2;
+ len -= ulen + wlen + 2;
+ printer(arg, " user=");
+ ppp_print_string(user, ulen, printer, arg);
+ printer(arg, " password=");
+/* FIXME: require ppp_pcb struct as printpkt() argument */
+#if 0
+ if (!pcb->settings.hide_password)
+#endif
+ ppp_print_string(pwd, wlen, printer, arg);
+#if 0
+ else
+ printer(arg, "<hidden>");
+#endif
+ break;
+ case UPAP_AUTHACK:
+ case UPAP_AUTHNAK:
+ if (len < 1)
+ break;
+ mlen = p[0];
+ if (len < mlen + 1)
+ break;
+ msg = (const u_char *) (p + 1);
+ p += mlen + 1;
+ len -= mlen + 1;
+ printer(arg, " ");
+ ppp_print_string(msg, mlen, printer, arg);
+ break;
+ default:
+ break;
+ }
+
+ /* print the rest of the bytes in the packet */
+ for (; len > 0; --len) {
+ GETCHAR(code, p);
+ printer(arg, " %.2x", code);
+ }
+
+ return p - pstart;
+}
+#endif /* PRINTPKT_SUPPORT */
+
+#endif /* PPP_SUPPORT && PAP_SUPPORT */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/utils.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/utils.c
new file mode 100644
index 0000000..008c633
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/utils.c
@@ -0,0 +1,959 @@
+/*
+ * utils.c - various utility functions used in pppd.
+ *
+ * Copyright (c) 1999-2002 Paul Mackerras. 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. The name(s) of the authors of this software must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission.
+ *
+ * 3. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by Paul Mackerras
+ * <paulus@samba.org>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#if 0 /* UNUSED */
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <netdb.h>
+#include <time.h>
+#include <utmp.h>
+#include <pwd.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#ifdef SVR4
+#include <sys/mkdev.h>
+#endif
+#endif /* UNUSED */
+
+#include <ctype.h> /* isdigit() */
+
+#include "netif/ppp/ppp_impl.h"
+
+#include "netif/ppp/fsm.h"
+#include "netif/ppp/lcp.h"
+
+#if defined(SUNOS4)
+extern char *strerror();
+#endif
+
+static void ppp_logit(int level, const char *fmt, va_list args);
+static void ppp_log_write(int level, char *buf);
+#if PRINTPKT_SUPPORT
+static void ppp_vslp_printer(void *arg, const char *fmt, ...);
+static void ppp_format_packet(const u_char *p, int len,
+ void (*printer) (void *, const char *, ...), void *arg);
+
+struct buffer_info {
+ char *ptr;
+ int len;
+};
+#endif /* PRINTPKT_SUPPORT */
+
+/*
+ * ppp_strlcpy - like strcpy/strncpy, doesn't overflow destination buffer,
+ * always leaves destination null-terminated (for len > 0).
+ */
+size_t ppp_strlcpy(char *dest, const char *src, size_t len) {
+ size_t ret = strlen(src);
+
+ if (len != 0) {
+ if (ret < len)
+ strcpy(dest, src);
+ else {
+ strncpy(dest, src, len - 1);
+ dest[len-1] = 0;
+ }
+ }
+ return ret;
+}
+
+/*
+ * ppp_strlcat - like strcat/strncat, doesn't overflow destination buffer,
+ * always leaves destination null-terminated (for len > 0).
+ */
+size_t ppp_strlcat(char *dest, const char *src, size_t len) {
+ size_t dlen = strlen(dest);
+
+ return dlen + ppp_strlcpy(dest + dlen, src, (len > dlen? len - dlen: 0));
+}
+
+
+/*
+ * ppp_slprintf - format a message into a buffer. Like sprintf except we
+ * also specify the length of the output buffer, and we handle
+ * %m (error message), %v (visible string),
+ * %q (quoted string), %t (current time) and %I (IP address) formats.
+ * Doesn't do floating-point formats.
+ * Returns the number of chars put into buf.
+ */
+int ppp_slprintf(char *buf, int buflen, const char *fmt, ...) {
+ va_list args;
+ int n;
+
+ va_start(args, fmt);
+ n = ppp_vslprintf(buf, buflen, fmt, args);
+ va_end(args);
+ return n;
+}
+
+/*
+ * ppp_vslprintf - like ppp_slprintf, takes a va_list instead of a list of args.
+ */
+#define OUTCHAR(c) (buflen > 0? (--buflen, *buf++ = (c)): 0)
+
+int ppp_vslprintf(char *buf, int buflen, const char *fmt, va_list args) {
+ int c, i, n;
+ int width, prec, fillch;
+ int base, len, neg, quoted;
+ unsigned long val = 0;
+ const char *f;
+ char *str, *buf0;
+ const unsigned char *p;
+ char num[32];
+#if 0 /* need port */
+ time_t t;
+#endif /* need port */
+ u32_t ip;
+ static char hexchars[] = "0123456789abcdef";
+#if PRINTPKT_SUPPORT
+ struct buffer_info bufinfo;
+#endif /* PRINTPKT_SUPPORT */
+
+ buf0 = buf;
+ --buflen;
+ while (buflen > 0) {
+ for (f = fmt; *f != '%' && *f != 0; ++f)
+ ;
+ if (f > fmt) {
+ len = f - fmt;
+ if (len > buflen)
+ len = buflen;
+ memcpy(buf, fmt, len);
+ buf += len;
+ buflen -= len;
+ fmt = f;
+ }
+ if (*fmt == 0)
+ break;
+ c = *++fmt;
+ width = 0;
+ prec = -1;
+ fillch = ' ';
+ if (c == '0') {
+ fillch = '0';
+ c = *++fmt;
+ }
+ if (c == '*') {
+ width = va_arg(args, int);
+ c = *++fmt;
+ } else {
+ while (isdigit(c)) {
+ width = width * 10 + c - '0';
+ c = *++fmt;
+ }
+ }
+ if (c == '.') {
+ c = *++fmt;
+ if (c == '*') {
+ prec = va_arg(args, int);
+ c = *++fmt;
+ } else {
+ prec = 0;
+ while (isdigit(c)) {
+ prec = prec * 10 + c - '0';
+ c = *++fmt;
+ }
+ }
+ }
+ str = 0;
+ base = 0;
+ neg = 0;
+ ++fmt;
+ switch (c) {
+ case 'l':
+ c = *fmt++;
+ switch (c) {
+ case 'd':
+ val = va_arg(args, long);
+ if ((long)val < 0) {
+ neg = 1;
+ val = (unsigned long)-(long)val;
+ }
+ base = 10;
+ break;
+ case 'u':
+ val = va_arg(args, unsigned long);
+ base = 10;
+ break;
+ default:
+ OUTCHAR('%');
+ OUTCHAR('l');
+ --fmt; /* so %lz outputs %lz etc. */
+ continue;
+ }
+ break;
+ case 'd':
+ i = va_arg(args, int);
+ if (i < 0) {
+ neg = 1;
+ val = -i;
+ } else
+ val = i;
+ base = 10;
+ break;
+ case 'u':
+ val = va_arg(args, unsigned int);
+ base = 10;
+ break;
+ case 'o':
+ val = va_arg(args, unsigned int);
+ base = 8;
+ break;
+ case 'x':
+ case 'X':
+ val = va_arg(args, unsigned int);
+ base = 16;
+ break;
+#if 0 /* unused (and wrong on LLP64 systems) */
+ case 'p':
+ val = (unsigned long) va_arg(args, void *);
+ base = 16;
+ neg = 2;
+ break;
+#endif /* unused (and wrong on LLP64 systems) */
+ case 's':
+ str = va_arg(args, char *);
+ break;
+ case 'c':
+ num[0] = va_arg(args, int);
+ num[1] = 0;
+ str = num;
+ break;
+#if 0 /* do we always have strerror() in embedded ? */
+ case 'm':
+ str = strerror(errno);
+ break;
+#endif /* do we always have strerror() in embedded ? */
+ case 'I':
+ ip = va_arg(args, u32_t);
+ ip = lwip_ntohl(ip);
+ ppp_slprintf(num, sizeof(num), "%d.%d.%d.%d", (ip >> 24) & 0xff,
+ (ip >> 16) & 0xff, (ip >> 8) & 0xff, ip & 0xff);
+ str = num;
+ break;
+#if 0 /* need port */
+ case 't':
+ time(&t);
+ str = ctime(&t);
+ str += 4; /* chop off the day name */
+ str[15] = 0; /* chop off year and newline */
+ break;
+#endif /* need port */
+ case 'v': /* "visible" string */
+ case 'q': /* quoted string */
+ quoted = c == 'q';
+ p = va_arg(args, unsigned char *);
+ if (p == NULL)
+ p = (const unsigned char *)"<NULL>";
+ if (fillch == '0' && prec >= 0) {
+ n = prec;
+ } else {
+ n = strlen((const char *)p);
+ if (prec >= 0 && n > prec)
+ n = prec;
+ }
+ while (n > 0 && buflen > 0) {
+ c = *p++;
+ --n;
+ if (!quoted && c >= 0x80) {
+ OUTCHAR('M');
+ OUTCHAR('-');
+ c -= 0x80;
+ }
+ if (quoted && (c == '"' || c == '\\'))
+ OUTCHAR('\\');
+ if (c < 0x20 || (0x7f <= c && c < 0xa0)) {
+ if (quoted) {
+ OUTCHAR('\\');
+ switch (c) {
+ case '\t': OUTCHAR('t'); break;
+ case '\n': OUTCHAR('n'); break;
+ case '\b': OUTCHAR('b'); break;
+ case '\f': OUTCHAR('f'); break;
+ default:
+ OUTCHAR('x');
+ OUTCHAR(hexchars[c >> 4]);
+ OUTCHAR(hexchars[c & 0xf]);
+ }
+ } else {
+ if (c == '\t')
+ OUTCHAR(c);
+ else {
+ OUTCHAR('^');
+ OUTCHAR(c ^ 0x40);
+ }
+ }
+ } else
+ OUTCHAR(c);
+ }
+ continue;
+#if PRINTPKT_SUPPORT
+ case 'P': /* print PPP packet */
+ bufinfo.ptr = buf;
+ bufinfo.len = buflen + 1;
+ p = va_arg(args, unsigned char *);
+ n = va_arg(args, int);
+ ppp_format_packet(p, n, ppp_vslp_printer, &bufinfo);
+ buf = bufinfo.ptr;
+ buflen = bufinfo.len - 1;
+ continue;
+#endif /* PRINTPKT_SUPPORT */
+ case 'B':
+ p = va_arg(args, unsigned char *);
+ for (n = prec; n > 0; --n) {
+ c = *p++;
+ if (fillch == ' ')
+ OUTCHAR(' ');
+ OUTCHAR(hexchars[(c >> 4) & 0xf]);
+ OUTCHAR(hexchars[c & 0xf]);
+ }
+ continue;
+ default:
+ *buf++ = '%';
+ if (c != '%')
+ --fmt; /* so %z outputs %z etc. */
+ --buflen;
+ continue;
+ }
+ if (base != 0) {
+ str = num + sizeof(num);
+ *--str = 0;
+ while (str > num + neg) {
+ *--str = hexchars[val % base];
+ val = val / base;
+ if (--prec <= 0 && val == 0)
+ break;
+ }
+ switch (neg) {
+ case 1:
+ *--str = '-';
+ break;
+ case 2:
+ *--str = 'x';
+ *--str = '0';
+ break;
+ default:
+ break;
+ }
+ len = num + sizeof(num) - 1 - str;
+ } else {
+ len = strlen(str);
+ if (prec >= 0 && len > prec)
+ len = prec;
+ }
+ if (width > 0) {
+ if (width > buflen)
+ width = buflen;
+ if ((n = width - len) > 0) {
+ buflen -= n;
+ for (; n > 0; --n)
+ *buf++ = fillch;
+ }
+ }
+ if (len > buflen)
+ len = buflen;
+ memcpy(buf, str, len);
+ buf += len;
+ buflen -= len;
+ }
+ *buf = 0;
+ return buf - buf0;
+}
+
+#if PRINTPKT_SUPPORT
+/*
+ * vslp_printer - used in processing a %P format
+ */
+static void ppp_vslp_printer(void *arg, const char *fmt, ...) {
+ int n;
+ va_list pvar;
+ struct buffer_info *bi;
+
+ va_start(pvar, fmt);
+ bi = (struct buffer_info *) arg;
+ n = ppp_vslprintf(bi->ptr, bi->len, fmt, pvar);
+ va_end(pvar);
+
+ bi->ptr += n;
+ bi->len -= n;
+}
+#endif /* PRINTPKT_SUPPORT */
+
+#if 0 /* UNUSED */
+/*
+ * log_packet - format a packet and log it.
+ */
+
+void
+log_packet(p, len, prefix, level)
+ u_char *p;
+ int len;
+ char *prefix;
+ int level;
+{
+ init_pr_log(prefix, level);
+ ppp_format_packet(p, len, pr_log, &level);
+ end_pr_log();
+}
+#endif /* UNUSED */
+
+#if PRINTPKT_SUPPORT
+/*
+ * ppp_format_packet - make a readable representation of a packet,
+ * calling `printer(arg, format, ...)' to output it.
+ */
+static void ppp_format_packet(const u_char *p, int len,
+ void (*printer) (void *, const char *, ...), void *arg) {
+ int i, n;
+ u_short proto;
+ const struct protent *protp;
+
+ if (len >= 2) {
+ GETSHORT(proto, p);
+ len -= 2;
+ for (i = 0; (protp = protocols[i]) != NULL; ++i)
+ if (proto == protp->protocol)
+ break;
+ if (protp != NULL) {
+ printer(arg, "[%s", protp->name);
+ n = (*protp->printpkt)(p, len, printer, arg);
+ printer(arg, "]");
+ p += n;
+ len -= n;
+ } else {
+ for (i = 0; (protp = protocols[i]) != NULL; ++i)
+ if (proto == (protp->protocol & ~0x8000))
+ break;
+ if (protp != 0 && protp->data_name != 0) {
+ printer(arg, "[%s data]", protp->data_name);
+ if (len > 8)
+ printer(arg, "%.8B ...", p);
+ else
+ printer(arg, "%.*B", len, p);
+ len = 0;
+ } else
+ printer(arg, "[proto=0x%x]", proto);
+ }
+ }
+
+ if (len > 32)
+ printer(arg, "%.32B ...", p);
+ else
+ printer(arg, "%.*B", len, p);
+}
+#endif /* PRINTPKT_SUPPORT */
+
+#if 0 /* UNUSED */
+/*
+ * init_pr_log, end_pr_log - initialize and finish use of pr_log.
+ */
+
+static char line[256]; /* line to be logged accumulated here */
+static char *linep; /* current pointer within line */
+static int llevel; /* level for logging */
+
+void
+init_pr_log(prefix, level)
+ const char *prefix;
+ int level;
+{
+ linep = line;
+ if (prefix != NULL) {
+ ppp_strlcpy(line, prefix, sizeof(line));
+ linep = line + strlen(line);
+ }
+ llevel = level;
+}
+
+void
+end_pr_log()
+{
+ if (linep != line) {
+ *linep = 0;
+ ppp_log_write(llevel, line);
+ }
+}
+
+/*
+ * pr_log - printer routine for outputting to log
+ */
+void
+pr_log (void *arg, const char *fmt, ...)
+{
+ int l, n;
+ va_list pvar;
+ char *p, *eol;
+ char buf[256];
+
+ va_start(pvar, fmt);
+ n = ppp_vslprintf(buf, sizeof(buf), fmt, pvar);
+ va_end(pvar);
+
+ p = buf;
+ eol = strchr(buf, '\n');
+ if (linep != line) {
+ l = (eol == NULL)? n: eol - buf;
+ if (linep + l < line + sizeof(line)) {
+ if (l > 0) {
+ memcpy(linep, buf, l);
+ linep += l;
+ }
+ if (eol == NULL)
+ return;
+ p = eol + 1;
+ eol = strchr(p, '\n');
+ }
+ *linep = 0;
+ ppp_log_write(llevel, line);
+ linep = line;
+ }
+
+ while (eol != NULL) {
+ *eol = 0;
+ ppp_log_write(llevel, p);
+ p = eol + 1;
+ eol = strchr(p, '\n');
+ }
+
+ /* assumes sizeof(buf) <= sizeof(line) */
+ l = buf + n - p;
+ if (l > 0) {
+ memcpy(line, p, n);
+ linep = line + l;
+ }
+}
+#endif /* UNUSED */
+
+/*
+ * ppp_print_string - print a readable representation of a string using
+ * printer.
+ */
+void ppp_print_string(const u_char *p, int len, void (*printer) (void *, const 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", (u8_t)c);
+ /* no break */
+ }
+ }
+ }
+ printer(arg, "\"");
+}
+
+/*
+ * ppp_logit - does the hard work for fatal et al.
+ */
+static void ppp_logit(int level, const char *fmt, va_list args) {
+ char buf[1024];
+
+ ppp_vslprintf(buf, sizeof(buf), fmt, args);
+ ppp_log_write(level, buf);
+}
+
+static void ppp_log_write(int level, char *buf) {
+ LWIP_UNUSED_ARG(level); /* necessary if PPPDEBUG is defined to an empty function */
+ LWIP_UNUSED_ARG(buf);
+ PPPDEBUG(level, ("%s\n", buf) );
+#if 0
+ if (log_to_fd >= 0 && (level != LOG_DEBUG || debug)) {
+ int n = strlen(buf);
+
+ if (n > 0 && buf[n-1] == '\n')
+ --n;
+ if (write(log_to_fd, buf, n) != n
+ || write(log_to_fd, "\n", 1) != 1)
+ log_to_fd = -1;
+ }
+#endif
+}
+
+/*
+ * ppp_fatal - log an error message and die horribly.
+ */
+void ppp_fatal(const char *fmt, ...) {
+ va_list pvar;
+
+ va_start(pvar, fmt);
+ ppp_logit(LOG_ERR, fmt, pvar);
+ va_end(pvar);
+
+ LWIP_ASSERT("ppp_fatal", 0); /* as promised */
+}
+
+/*
+ * ppp_error - log an error message.
+ */
+void ppp_error(const char *fmt, ...) {
+ va_list pvar;
+
+ va_start(pvar, fmt);
+ ppp_logit(LOG_ERR, fmt, pvar);
+ va_end(pvar);
+#if 0 /* UNUSED */
+ ++error_count;
+#endif /* UNUSED */
+}
+
+/*
+ * ppp_warn - log a warning message.
+ */
+void ppp_warn(const char *fmt, ...) {
+ va_list pvar;
+
+ va_start(pvar, fmt);
+ ppp_logit(LOG_WARNING, fmt, pvar);
+ va_end(pvar);
+}
+
+/*
+ * ppp_notice - log a notice-level message.
+ */
+void ppp_notice(const char *fmt, ...) {
+ va_list pvar;
+
+ va_start(pvar, fmt);
+ ppp_logit(LOG_NOTICE, fmt, pvar);
+ va_end(pvar);
+}
+
+/*
+ * ppp_info - log an informational message.
+ */
+void ppp_info(const char *fmt, ...) {
+ va_list pvar;
+
+ va_start(pvar, fmt);
+ ppp_logit(LOG_INFO, fmt, pvar);
+ va_end(pvar);
+}
+
+/*
+ * ppp_dbglog - log a debug message.
+ */
+void ppp_dbglog(const char *fmt, ...) {
+ va_list pvar;
+
+ va_start(pvar, fmt);
+ ppp_logit(LOG_DEBUG, fmt, pvar);
+ va_end(pvar);
+}
+
+#if PRINTPKT_SUPPORT
+/*
+ * ppp_dump_packet - print out a packet in readable form if it is interesting.
+ * Assumes len >= PPP_HDRLEN.
+ */
+void ppp_dump_packet(ppp_pcb *pcb, const char *tag, unsigned char *p, int len) {
+ int proto;
+
+ /*
+ * don't print data packets, i.e. IPv4, IPv6, VJ, and compressed packets.
+ */
+ proto = (p[0] << 8) + p[1];
+ if (proto < 0xC000 && (proto & ~0x8000) == proto)
+ return;
+
+ /*
+ * don't print valid LCP echo request/reply packets if the link is up.
+ */
+ if (proto == PPP_LCP && pcb->phase == PPP_PHASE_RUNNING && len >= 2 + HEADERLEN) {
+ unsigned char *lcp = p + 2;
+ int l = (lcp[2] << 8) + lcp[3];
+
+ if ((lcp[0] == ECHOREQ || lcp[0] == ECHOREP)
+ && l >= HEADERLEN && l <= len - 2)
+ return;
+ }
+
+ ppp_dbglog("%s %P", tag, p, len);
+}
+#endif /* PRINTPKT_SUPPORT */
+
+#if 0 /* Unused */
+
+/*
+ * complete_read - read a full `count' bytes from fd,
+ * unless end-of-file or an error other than EINTR is encountered.
+ */
+ssize_t
+complete_read(int fd, void *buf, size_t count)
+{
+ size_t done;
+ ssize_t nb;
+ char *ptr = buf;
+
+ for (done = 0; done < count; ) {
+ nb = read(fd, ptr, count - done);
+ if (nb < 0) {
+ if (errno == EINTR)
+ continue;
+ return -1;
+ }
+ if (nb == 0)
+ break;
+ done += nb;
+ ptr += nb;
+ }
+ return done;
+}
+
+/* Procedures for locking the serial device using a lock file. */
+#ifndef LOCK_DIR
+#ifdef __linux__
+#define LOCK_DIR "/var/lock"
+#else
+#ifdef SVR4
+#define LOCK_DIR "/var/spool/locks"
+#else
+#define LOCK_DIR "/var/spool/lock"
+#endif
+#endif
+#endif /* LOCK_DIR */
+
+static char lock_file[MAXPATHLEN];
+
+/*
+ * lock - create a lock file for the named device
+ */
+int
+lock(dev)
+ char *dev;
+{
+#ifdef LOCKLIB
+ int result;
+
+ result = mklock (dev, (void *) 0);
+ if (result == 0) {
+ ppp_strlcpy(lock_file, dev, sizeof(lock_file));
+ return 0;
+ }
+
+ if (result > 0)
+ ppp_notice("Device %s is locked by pid %d", dev, result);
+ else
+ ppp_error("Can't create lock file %s", lock_file);
+ return -1;
+
+#else /* LOCKLIB */
+
+ char lock_buffer[12];
+ int fd, pid, n;
+
+#ifdef SVR4
+ struct stat sbuf;
+
+ if (stat(dev, &sbuf) < 0) {
+ ppp_error("Can't get device number for %s: %m", dev);
+ return -1;
+ }
+ if ((sbuf.st_mode & S_IFMT) != S_IFCHR) {
+ ppp_error("Can't lock %s: not a character device", dev);
+ return -1;
+ }
+ ppp_slprintf(lock_file, sizeof(lock_file), "%s/LK.%03d.%03d.%03d",
+ LOCK_DIR, major(sbuf.st_dev),
+ major(sbuf.st_rdev), minor(sbuf.st_rdev));
+#else
+ char *p;
+ char lockdev[MAXPATHLEN];
+
+ if ((p = strstr(dev, "dev/")) != NULL) {
+ dev = p + 4;
+ strncpy(lockdev, dev, MAXPATHLEN-1);
+ lockdev[MAXPATHLEN-1] = 0;
+ while ((p = strrchr(lockdev, '/')) != NULL) {
+ *p = '_';
+ }
+ dev = lockdev;
+ } else
+ if ((p = strrchr(dev, '/')) != NULL)
+ dev = p + 1;
+
+ ppp_slprintf(lock_file, sizeof(lock_file), "%s/LCK..%s", LOCK_DIR, dev);
+#endif
+
+ while ((fd = open(lock_file, O_EXCL | O_CREAT | O_RDWR, 0644)) < 0) {
+ if (errno != EEXIST) {
+ ppp_error("Can't create lock file %s: %m", lock_file);
+ break;
+ }
+
+ /* Read the lock file to find out who has the device locked. */
+ fd = open(lock_file, O_RDONLY, 0);
+ if (fd < 0) {
+ if (errno == ENOENT) /* This is just a timing problem. */
+ continue;
+ ppp_error("Can't open existing lock file %s: %m", lock_file);
+ break;
+ }
+#ifndef LOCK_BINARY
+ n = read(fd, lock_buffer, 11);
+#else
+ n = read(fd, &pid, sizeof(pid));
+#endif /* LOCK_BINARY */
+ close(fd);
+ fd = -1;
+ if (n <= 0) {
+ ppp_error("Can't read pid from lock file %s", lock_file);
+ break;
+ }
+
+ /* See if the process still exists. */
+#ifndef LOCK_BINARY
+ lock_buffer[n] = 0;
+ pid = atoi(lock_buffer);
+#endif /* LOCK_BINARY */
+ if (pid == getpid())
+ return 1; /* somebody else locked it for us */
+ if (pid == 0
+ || (kill(pid, 0) == -1 && errno == ESRCH)) {
+ if (unlink (lock_file) == 0) {
+ ppp_notice("Removed stale lock on %s (pid %d)", dev, pid);
+ continue;
+ }
+ ppp_warn("Couldn't remove stale lock on %s", dev);
+ } else
+ ppp_notice("Device %s is locked by pid %d", dev, pid);
+ break;
+ }
+
+ if (fd < 0) {
+ lock_file[0] = 0;
+ return -1;
+ }
+
+ pid = getpid();
+#ifndef LOCK_BINARY
+ ppp_slprintf(lock_buffer, sizeof(lock_buffer), "%10d\n", pid);
+ write (fd, lock_buffer, 11);
+#else
+ write(fd, &pid, sizeof (pid));
+#endif
+ close(fd);
+ return 0;
+
+#endif
+}
+
+/*
+ * relock - called to update our lockfile when we are about to detach,
+ * thus changing our pid (we fork, the child carries on, and the parent dies).
+ * Note that this is called by the parent, with pid equal to the pid
+ * of the child. This avoids a potential race which would exist if
+ * we had the child rewrite the lockfile (the parent might die first,
+ * and another process could think the lock was stale if it checked
+ * between when the parent died and the child rewrote the lockfile).
+ */
+int
+relock(pid)
+ int pid;
+{
+#ifdef LOCKLIB
+ /* XXX is there a way to do this? */
+ return -1;
+#else /* LOCKLIB */
+
+ int fd;
+ char lock_buffer[12];
+
+ if (lock_file[0] == 0)
+ return -1;
+ fd = open(lock_file, O_WRONLY, 0);
+ if (fd < 0) {
+ ppp_error("Couldn't reopen lock file %s: %m", lock_file);
+ lock_file[0] = 0;
+ return -1;
+ }
+
+#ifndef LOCK_BINARY
+ ppp_slprintf(lock_buffer, sizeof(lock_buffer), "%10d\n", pid);
+ write (fd, lock_buffer, 11);
+#else
+ write(fd, &pid, sizeof(pid));
+#endif /* LOCK_BINARY */
+ close(fd);
+ return 0;
+
+#endif /* LOCKLIB */
+}
+
+/*
+ * unlock - remove our lockfile
+ */
+void
+unlock()
+{
+ if (lock_file[0]) {
+#ifdef LOCKLIB
+ (void) rmlock(lock_file, (void *) 0);
+#else
+ unlink(lock_file);
+#endif
+ lock_file[0] = 0;
+ }
+}
+
+#endif /* Unused */
+
+#endif /* PPP_SUPPORT */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/vj.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/vj.c
new file mode 100644
index 0000000..168c340
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/ppp/vj.c
@@ -0,0 +1,695 @@
+/*
+ * 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 "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT && VJ_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#include "netif/ppp/ppp_impl.h"
+#include "netif/ppp/pppdebug.h"
+
+#include "netif/ppp/vj.h"
+
+#include <string.h>
+
+#if LINK_STATS
+#define INCR(counter) ++comp->stats.counter
+#else
+#define INCR(counter)
+#endif
+
+void
+vj_compress_init(struct vjcompress *comp)
+{
+ u8_t i;
+ 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 ((u16_t)(n) >= 256) { \
+ *cp++ = 0; \
+ cp[1] = (u8_t)(n); \
+ cp[0] = (u8_t)((n) >> 8); \
+ cp += 2; \
+ } else { \
+ *cp++ = (u8_t)(n); \
+ } \
+}
+#define ENCODEZ(n) { \
+ if ((u16_t)(n) >= 256 || (u16_t)(n) == 0) { \
+ *cp++ = 0; \
+ cp[1] = (u8_t)(n); \
+ cp[0] = (u8_t)((n) >> 8); \
+ cp += 2; \
+ } else { \
+ *cp++ = (u8_t)(n); \
+ } \
+}
+
+#define DECODEL(f) { \
+ if (*cp == 0) {\
+ u32_t tmp_ = lwip_ntohl(f) + ((cp[1] << 8) | cp[2]); \
+ (f) = lwip_htonl(tmp_); \
+ cp += 3; \
+ } else { \
+ u32_t tmp_ = lwip_ntohl(f) + (u32_t)*cp++; \
+ (f) = lwip_htonl(tmp_); \
+ } \
+}
+
+#define DECODES(f) { \
+ if (*cp == 0) {\
+ u16_t tmp_ = lwip_ntohs(f) + (((u16_t)cp[1] << 8) | cp[2]); \
+ (f) = lwip_htons(tmp_); \
+ cp += 3; \
+ } else { \
+ u16_t tmp_ = lwip_ntohs(f) + (u16_t)*cp++; \
+ (f) = lwip_htons(tmp_); \
+ } \
+}
+
+#define DECODEU(f) { \
+ if (*cp == 0) {\
+ (f) = lwip_htons(((u16_t)cp[1] << 8) | cp[2]); \
+ cp += 3; \
+ } else { \
+ (f) = lwip_htons((u16_t)*cp++); \
+ } \
+}
+
+/* Helper structures for unaligned *u32_t and *u16_t accesses */
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct vj_u32_t {
+ PACK_STRUCT_FIELD(u32_t v);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct vj_u16_t {
+ PACK_STRUCT_FIELD(u16_t v);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+/*
+ * 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.
+ */
+u8_t
+vj_compress_tcp(struct vjcompress *comp, struct pbuf **pb)
+{
+ struct pbuf *np = *pb;
+ struct ip_hdr *ip = (struct ip_hdr *)np->payload;
+ struct cstate *cs = comp->last_cs->cs_next;
+ u16_t ilen = IPH_HL(ip);
+ u16_t hlen;
+ struct tcp_hdr *oth;
+ struct tcp_hdr *th;
+ u16_t deltaS, deltaA = 0;
+ u32_t deltaL;
+ u32_t changes = 0;
+ u8_t new_seq[16];
+ u8_t *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)) || np->tot_len < 40) {
+ return (TYPE_IP);
+ }
+ th = (struct tcp_hdr *)&((struct vj_u32_t*)ip)[ilen];
+ if ((TCPH_FLAGS(th) & (TCP_SYN|TCP_FIN|TCP_RST|TCP_ACK)) != TCP_ACK) {
+ return (TYPE_IP);
+ }
+
+ /* Check that the TCP/IP headers are contained in the first buffer. */
+ hlen = ilen + TCPH_HDRLEN(th);
+ hlen <<= 2;
+ if (np->len < hlen) {
+ PPPDEBUG(LOG_INFO, ("vj_compress_tcp: header len %d spans buffers\n", hlen));
+ return (TYPE_IP);
+ }
+
+ /* TCP stack requires that we don't change the packet payload, therefore we copy
+ * the whole packet before compression. */
+ np = pbuf_alloc(PBUF_RAW, np->tot_len, PBUF_POOL);
+ if (!np) {
+ return (TYPE_IP);
+ }
+
+ if (pbuf_copy(np, *pb) != ERR_OK) {
+ pbuf_free(np);
+ return (TYPE_IP);
+ }
+
+ *pb = np;
+ ip = (struct ip_hdr *)np->payload;
+
+ /*
+ * 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 (!ip4_addr_cmp(&ip->src, &cs->cs_ip.src)
+ || !ip4_addr_cmp(&ip->dest, &cs->cs_ip.dest)
+ || (*(struct vj_u32_t*)th).v != (((struct vj_u32_t*)&cs->cs_ip)[IPH_HL(&cs->cs_ip)]).v) {
+ /*
+ * 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.
+ */
+ struct cstate *lcs;
+ struct cstate *lastcs = comp->last_cs;
+
+ do {
+ lcs = cs; cs = cs->cs_next;
+ INCR(vjs_searches);
+ if (ip4_addr_cmp(&ip->src, &cs->cs_ip.src)
+ && ip4_addr_cmp(&ip->dest, &cs->cs_ip.dest)
+ && (*(struct vj_u32_t*)th).v == (((struct vj_u32_t*)&cs->cs_ip)[IPH_HL(&cs->cs_ip)]).v) {
+ 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;
+ 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 *)&((struct vj_u32_t*)&cs->cs_ip)[ilen];
+ deltaS = ilen;
+
+ /*
+ * 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 ((((struct vj_u16_t*)ip)[0]).v != (((struct vj_u16_t*)&cs->cs_ip)[0]).v
+ || (((struct vj_u16_t*)ip)[3]).v != (((struct vj_u16_t*)&cs->cs_ip)[3]).v
+ || (((struct vj_u16_t*)ip)[4]).v != (((struct vj_u16_t*)&cs->cs_ip)[4]).v
+ || 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 = lwip_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 = (u16_t)(lwip_ntohs(th->wnd) - lwip_ntohs(oth->wnd))) != 0) {
+ ENCODE(deltaS);
+ changes |= NEW_W;
+ }
+
+ if ((deltaL = lwip_ntohl(th->ackno) - lwip_ntohl(oth->ackno)) != 0) {
+ if (deltaL > 0xffff) {
+ goto uncompressed;
+ }
+ deltaA = (u16_t)deltaL;
+ ENCODE(deltaA);
+ changes |= NEW_A;
+ }
+
+ if ((deltaL = lwip_ntohl(th->seqno) - lwip_ntohl(oth->seqno)) != 0) {
+ if (deltaL > 0xffff) {
+ goto uncompressed;
+ }
+ deltaS = (u16_t)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) &&
+ lwip_ntohs(IPH_LEN(&cs->cs_ip)) == hlen) {
+ break;
+ }
+ /* no 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 == lwip_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 == lwip_ntohs(IPH_LEN(&cs->cs_ip)) - hlen) {
+ /* special case for data xfer */
+ changes = SPECIAL_D;
+ cp = new_seq;
+ }
+ break;
+ default:
+ break;
+ }
+
+ deltaS = (u16_t)(lwip_ntohs(IPH_ID(ip)) - lwip_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 = lwip_ntohs(th->chksum);
+ MEMCPY(&cs->cs_ip, 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 = (u16_t)(cp - new_seq);
+ if (!comp->compressSlot || comp->last_xmit != cs->cs_id) {
+ comp->last_xmit = cs->cs_id;
+ hlen -= deltaS + 4;
+ if (pbuf_header(np, -(s16_t)hlen)){
+ /* Can we cope with this failing? Just assert for now */
+ LWIP_ASSERT("pbuf_header failed\n", 0);
+ }
+ cp = (u8_t*)np->payload;
+ *cp++ = (u8_t)(changes | NEW_C);
+ *cp++ = cs->cs_id;
+ } else {
+ hlen -= deltaS + 3;
+ if (pbuf_header(np, -(s16_t)hlen)) {
+ /* Can we cope with this failing? Just assert for now */
+ LWIP_ASSERT("pbuf_header failed\n", 0);
+ }
+ cp = (u8_t*)np->payload;
+ *cp++ = (u8_t)changes;
+ }
+ *cp++ = (u8_t)(deltaA >> 8);
+ *cp++ = (u8_t)deltaA;
+ MEMCPY(cp, new_seq, 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:
+ MEMCPY(&cs->cs_ip, 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)
+{
+ u32_t hlen;
+ struct cstate *cs;
+ 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);
+ MEMCPY(&cs->cs_ip, ip, hlen);
+ cs->cs_hlen = (u16_t)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)
+{
+ u8_t *cp;
+ struct tcp_hdr *th;
+ struct cstate *cs;
+ struct vj_u16_t *bp;
+ struct pbuf *n0 = *nb;
+ u32_t tmp;
+ u32_t vjlen, hlen, changes;
+
+ INCR(vjs_compressedin);
+ cp = (u8_t*)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 *)&((u8_t*)&cs->cs_ip)[hlen];
+ th->chksum = lwip_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:
+ {
+ u32_t i = lwip_ntohs(IPH_LEN(&cs->cs_ip)) - cs->cs_hlen;
+ /* some compilers can't nest inline assembler.. */
+ tmp = lwip_ntohl(th->ackno) + i;
+ th->ackno = lwip_htonl(tmp);
+ tmp = lwip_ntohl(th->seqno) + i;
+ th->seqno = lwip_htonl(tmp);
+ }
+ break;
+
+ case SPECIAL_D:
+ /* some compilers can't nest inline assembler.. */
+ tmp = lwip_ntohl(th->seqno) + lwip_ntohs(IPH_LEN(&cs->cs_ip)) - cs->cs_hlen;
+ th->seqno = lwip_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, lwip_ntohs(IPH_ID(&cs->cs_ip)) + 1);
+ IPH_ID_SET(&cs->cs_ip, lwip_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 = (u16_t)(cp - (u8_t*)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, lwip_htons((u16_t)tmp));
+#else
+ IPH_LEN_SET(&cs->cs_ip, lwip_htons(n0->tot_len - vjlen + cs->cs_hlen));
+#endif
+
+ /* recompute the ip header checksum */
+ bp = (struct vj_u16_t*) &cs->cs_ip;
+ IPH_CHKSUM_SET(&cs->cs_ip, 0);
+ for (tmp = 0; hlen > 0; hlen -= 2) {
+ tmp += (*bp++).v;
+ }
+ tmp = (tmp & 0xffff) + (tmp >> 16);
+ tmp = (tmp & 0xffff) + (tmp >> 16);
+ IPH_CHKSUM_SET(&cs->cs_ip, (u16_t)(~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;
+
+#if IP_FORWARD
+ /* If IP forwarding is enabled we are using a PBUF_LINK packet type so
+ * the packet is being allocated with enough header space to be
+ * forwarded (to Ethernet for example).
+ */
+ np = pbuf_alloc(PBUF_LINK, n0->len + cs->cs_hlen, PBUF_POOL);
+#else /* IP_FORWARD */
+ np = pbuf_alloc(PBUF_RAW, n0->len + cs->cs_hlen, PBUF_POOL);
+#endif /* IP_FORWARD */
+ if(!np) {
+ PPPDEBUG(LOG_WARNING, ("vj_uncompress_tcp: realign failed\n"));
+ goto bad;
+ }
+
+ if (pbuf_header(np, -(s16_t)cs->cs_hlen)) {
+ /* Can we cope with this failing? Just assert for now */
+ LWIP_ASSERT("pbuf_header failed\n", 0);
+ goto bad;
+ }
+
+ bufptr = (u8_t*)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, (s16_t)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 /* PPP_SUPPORT && VJ_SUPPORT */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/slipif.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/slipif.c
new file mode 100644
index 0000000..6eb83c3
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/netif/slipif.c
@@ -0,0 +1,555 @@
+/**
+ * @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
+ */
+
+
+/**
+ * @defgroup slipif SLIP netif
+ * @ingroup addons
+ *
+ * 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
+ *
+ * Usage: This netif can be used in three ways:\n
+ * 1) For NO_SYS==0, an RX thread can be used which blocks on sio_read()
+ * until data is received.\n
+ * 2) In your main loop, call slipif_poll() to check for new RX bytes,
+ * completed packets are fed into netif->input().\n
+ * 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!).
+ *
+ */
+
+#include "netif/slipif.h"
+#include "lwip/opt.h"
+
+#include "lwip/def.h"
+#include "lwip/pbuf.h"
+#include "lwip/stats.h"
+#include "lwip/snmp.h"
+#include "lwip/sys.h"
+#include "lwip/sio.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 chain packet to send
+ * @return always returns ERR_OK since the serial layer does not provide return values
+ */
+static err_t
+slipif_output(struct netif *netif, struct pbuf *p)
+{
+ struct slipif_priv *priv;
+ struct pbuf *q;
+ u16_t i;
+ u8_t c;
+
+ LWIP_ASSERT("netif != NULL", (netif != NULL));
+ LWIP_ASSERT("netif->state != NULL", (netif->state != NULL));
+ LWIP_ASSERT("p != NULL", (p != NULL));
+
+ LWIP_DEBUGF(SLIP_DEBUG, ("slipif_output(%"U16_F"): sending %"U16_F" bytes\n", (u16_t)netif->num, p->tot_len));
+ priv = (struct slipif_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;
+}
+
+#if LWIP_IPV4
+/**
+ * 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 chain packet to send
+ * @param ipaddr the ip address to send the packet to (not used for slipif)
+ * @return always returns ERR_OK since the serial layer does not provide return values
+ */
+static err_t
+slipif_output_v4(struct netif *netif, struct pbuf *p, const ip4_addr_t *ipaddr)
+{
+ LWIP_UNUSED_ARG(ipaddr);
+ return slipif_output(netif, p);
+}
+#endif /* LWIP_IPV4 */
+
+#if LWIP_IPV6
+/**
+ * Send a pbuf doing the necessary SLIP encapsulation
+ *
+ * Uses the serial layer's sio_send()
+ *
+ * @param netif the lwip network interface structure for this slipif
+ * @param p the pbuf chain packet to send
+ * @param ipaddr the ip address to send the packet to (not used for slipif)
+ * @return always returns ERR_OK since the serial layer does not provide return values
+ */
+static err_t
+slipif_output_v6(struct netif *netif, struct pbuf *p, const ip6_addr_t *ipaddr)
+{
+ LWIP_UNUSED_ARG(ipaddr);
+ return slipif_output(netif, p);
+}
+#endif /* LWIP_IPV6 */
+
+/**
+ * Handle the incoming SLIP stream character by character
+ *
+ * @param netif the lwip network interface structure for this slipif
+ * @param c received character (multiple calls to this function will
+ * return a complete packet, NULL is returned before - used for polling)
+ * @return The IP packet when SLIP_END is received
+ */
+static struct pbuf*
+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 = (struct slipif_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;
+ default:
+ break;
+ } /* 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;
+ default:
+ break;
+ }
+ priv->state = SLIP_RECV_NORMAL;
+ break;
+ default:
+ 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_LINK_ENCAPSULATION_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 c 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';
+#if LWIP_IPV4
+ netif->output = slipif_output_v4;
+#endif /* LWIP_IPV4 */
+#if LWIP_IPV6
+ netif->output_ip6 = slipif_output_v6;
+#endif /* LWIP_IPV6 */
+ netif->mtu = SLIP_MAX_SIZE;
+
+ /* 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 */
+ MIB2_INIT_NETIF(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 */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/port/arch/cc.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/port/arch/cc.h
new file mode 100644
index 0000000..1d37d91
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/port/arch/cc.h
@@ -0,0 +1,85 @@
+/**
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Copyright (c) 2014 Nordic Semiconductor. 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.
+ *
+ */
+
+#ifndef __ARCH_CC_H__
+#define __ARCH_CC_H__
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+// Includes definition of mch_printf macro to do printf
+#include "nordic_common.h"
+#include "nrf.h"
+#include "compiler_abstraction.h"
+#include "nrf_assert.h"
+#include "errno.h"
+#include "app_util.h"
+
+#define BYTE_ORDER LITTLE_ENDIAN
+
+typedef uint8_t u8_t;
+typedef int8_t s8_t;
+typedef uint16_t u16_t;
+typedef int16_t s16_t;
+typedef uint32_t u32_t;
+typedef int32_t s32_t;
+
+typedef uintptr_t mem_ptr_t;
+
+#define LWIP_ERR_T int
+
+/* 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 structures */
+//#define PACK_STRUCT_FIELD __attribute__((packed))
+//#define PACK_STRUCT_FLD_8 __attribute__((packed))
+//#define PACK_STRUCT_STRUCT __attribute__((packed))
+#define PACK_STRUCT_BEGIN __attribute__((packed))
+//#define PACK_STRUCT_END
+
+void nrf_message(const char * m);
+
+/* Plaform specific diagnostic output */
+#define LWIP_PLATFORM_DIAG(x)
+
+#define LWIP_PLATFORM_ASSERT(x) ASSERT(x)
+
+#define LWIP_RAND() ((u32_t)rand())
+
+#endif /* __ARCH_CC_H__ */
+
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/port/arch/lwipopts.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/port/arch/lwipopts.h
new file mode 100644
index 0000000..a0819cb
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/port/arch/lwipopts.h
@@ -0,0 +1,157 @@
+/**
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Copyright (c) 2014 Nordic Semiconductor. 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.
+ *
+ */
+
+#ifndef __LWIPOPTS_H__
+#define __LWIPOPTS_H__
+
+#include "mem_manager.h"
+
+#define NO_SYS 1
+#define NO_SYS_NO_TIMERS 0
+#define MEM_LIBC_MALLOC 1
+#define MEMP_MEM_MALLOC 1
+#define MEM_ALIGNMENT 4
+#define MEM_SIZE 0
+#define PBUF_POOL_SIZE 32
+#define LWIP_ARP 0
+#define IP_REASS_MAX_PBUFS 0
+#define IP_FRAG_USES_STATIC_BUF 0
+#define MEMP_NUM_RAW_PCB 0
+#define MEMP_NUM_UDP_PCB 2
+#define MEMP_NUM_TCP_PCB 1
+#define MEMP_NUM_TCP_PCB_LISTEN 1
+#define MEMP_NUM_TCP_SEG 0
+#define MEMP_NUM_FRAG_PBUF 0
+#define MEMP_NUM_TCPIP_MSG_API 0
+#define MEMP_NUM_TCPIP_MSG_INPKT 0
+#define MEMP_NUM_SNMP_NODE 0
+#define MEMP_NUM_SNMP_ROOTNODE 0
+#define MEMP_NUM_SNMP_VARBIND 0
+#define MEMP_NUM_SNMP_VALUE 0
+#define IP_DEFAULT_TTL 255
+#define IP_SOF_BROADCAST 0
+#define IP_SOF_BROADCAST_RECV 0
+#define LWIP_ICMP 1
+#define LWIP_BROADCAST_PING 0
+#define LWIP_MULTICAST_PING 0
+#define LWIP_RAW 0
+#define TCP_LISTEN_BACKLOG 1
+#define LWIP_NETIF_STATUS_CALLBACK 1
+#define LWIP_NETIF_LINK_CALLBACK 1
+#define LWIP_NETIF_HWADDRHINT 1
+#define LWIP_NETCONN 0
+#define LWIP_SOCKET 0
+#define LWIP_STATS_DISPLAY 0
+#define MEM_STATS 0
+#define SYS_STATS 0
+#define MEMP_STATS 0
+#define LINK_STATS 0
+#define ETHARP_TRUST_IP_MAC 0
+#define ETH_PAD_SIZE 2
+#define LWIP_CHKSUM_ALGORITHM 2
+#define LWIP_CHECKSUM_ON_COPY 1
+#define LWIP_ND6_MAX_MULTICAST_SOLICIT 1
+#define LWIP_ND6_MAX_NEIGHBOR_ADVERTISEMENT 1
+#define LWIP_ND6_RETRANS_TIMER 20000
+#define LWIP_ND6_QUEUEING 0
+#define LWIP_ND6_NUM_ROUTERS 1
+#define LWIP_ND6_DELAY_FIRST_PROBE_TIME 10000
+
+#define LWIP_TCP_KEEPALIVE 1
+
+#define LWIP_ARP 0
+#define ARP_TABLE_SIZE 0
+#define IP_REASSEMBLY 0
+#define IP_FRAG 0
+#define LWIP_TCP 1
+#define LWIP_IPV6 1
+#define LWIP_IPV4 0
+#define LWIP_ICMP6 1
+#define LWIP_IPV6_REASS 0
+#define LWIP_ND6_TCP_REACHABILITY_HINTS 0
+#define LWIP_IPV6_MLD 0
+#define LWIP_STATS 0
+#define PPP_IPV6_SUPPORT 0
+#define LWIP_PPP_API 0
+#define PPP_SUPPORT 0
+#define LWIP_DNS 1
+#define LWIP_SUPPORT_CUSTOM_PBUF 1
+#define LWIP_BTLE_6LOWPAN 1
+
+#define TCP_TMR_INTERVAL 50
+// Keepalive values, compliant with RFC 1122. Don't change this unless you know what you're doing
+#define TCP_KEEPIDLE_DEFAULT 10000UL // Default KEEPALIVE timer in milliseconds
+#define TCP_KEEPINTVL_DEFAULT 2000UL // Default Time between KEEPALIVE probes in milliseconds
+#define TCP_KEEPCNT_DEFAULT 9U // Default Counter for KEEPALIVE probes
+#define TCP_MSS 896
+#define TCP_WND (4 * TCP_MSS)
+#define TCP_SND_BUF (4 * TCP_MSS)
+
+
+#define SO_REUSE 1
+
+//#define mem_init(...)
+//#define mem_free(p) nrf_free((p))
+//#define mem_malloc(sz) nrf_malloc((sz))
+//#define mem_trim(p,sz) nrf_realloc((p),(sz))
+
+#define LWIP_DEBUG 0
+
+#define ETHARP_DEBUG LWIP_DBG_OFF
+#define NETIF_DEBUG LWIP_DBG_OFF
+#define PBUF_DEBUG LWIP_DBG_OFF
+#define API_LIB_DEBUG LWIP_DBG_OFF
+#define API_MSG_DEBUG LWIP_DBG_OFF
+#define SOCKETS_DEBUG LWIP_DBG_OFF
+#define ICMP_DEBUG LWIP_DBG_OFF
+#define INET_DEBUG LWIP_DBG_OFF
+#define IP_DEBUG LWIP_DBG_OFF
+#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_OFF
+#define TCP_DEBUG LWIP_DBG_OFF
+#define TCP_INPUT_DEBUG LWIP_DBG_OFF
+#define TCP_OUTPUT_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_FR_DEBUG LWIP_DBG_OFF
+#define TCP_QLEN_DEBUG LWIP_DBG_OFF
+#define TCP_RST_DEBUG LWIP_DBG_OFF
+#define UDP_DEBUG LWIP_DBG_OFF
+#define TCPIP_DEBUG LWIP_DBG_OFF
+#define PPP_DEBUG LWIP_DBG_OFF
+#define SLIP_DEBUG LWIP_DBG_OFF
+#define DHCP_DEBUG LWIP_DBG_OFF
+#define SYS_LIGHTWEIGHT_PROT 0
+
+#endif /* __LWIPOPTS_H__ */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/port/arch/perf.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/port/arch/perf.h
new file mode 100644
index 0000000..93b903a
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/port/arch/perf.h
@@ -0,0 +1,36 @@
+/**
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Copyright (c) 2014 Nordic Semiconductor. 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.
+ *
+ */
+#ifndef __PERF_H__
+#define __PERF_H__
+
+#define PERF_START /* null definition */
+#define PERF_STOP(x) /* null definition */
+
+#endif /* __PERF_H__ */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/port/arch/sys_arch.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/port/arch/sys_arch.h
new file mode 100644
index 0000000..1e1ee44
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/port/arch/sys_arch.h
@@ -0,0 +1,45 @@
+/**
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Copyright (c) 2014 Nordic Semiconductor. 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.
+ *
+ */
+
+#ifndef __ARCH_SYS_ARCH_H__
+#define __ARCH_SYS_ARCH_H__
+
+#define SYS_MBOX_NULL NULL
+#define SYS_SEM_NULL NULL
+
+typedef void * sys_prot_t;
+
+typedef void * sys_sem_t;
+
+typedef void * sys_mbox_t;
+
+typedef void * sys_thread_t;
+
+#endif /* __ARCH_SYS_ARCH_H__ */
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/port/nrf_platform_port.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/port/nrf_platform_port.c
new file mode 100644
index 0000000..b6cdd5d
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/port/nrf_platform_port.c
@@ -0,0 +1,352 @@
+/* Copyright (c) 2014 Nordic Semiconductor. All Rights Reserved.
+ *
+ * The information contained herein is property of Nordic Semiconductor ASA.
+ * Terms and conditions of usage are described in detail in NORDIC
+ * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT.
+ *
+ * Licensees are granted free, non-transferable use of the information. NO
+ * WARRANTY of ANY KIND is provided. This heading must NOT be removed from
+ * the file.
+ *
+ */
+
+#include "lwip/opt.h"
+#include "lwip/def.h"
+#include "lwip/mem.h"
+#include "lwip/pbuf.h"
+#include "lwip/stats.h"
+#include "lwip/netif.h"
+#include "lwip/ip6.h"
+#include "sdk_config.h"
+#include "app_timer.h"
+#include "ble_ipsp.h"
+#include "ble_6lowpan.h"
+#include "mem_manager.h"
+#include "iot_context_manager.h"
+#include "nrf_platform_port.h"
+
+/**
+ * @defgroup nrf_driver_debug_log Module's Log Macros
+ * @details Macros used for creating module logs which can be useful in understanding handling
+ * of events or actions on API requests. These are intended for debugging purposes and
+ * can be enabled by defining the NRF_DRIVER_ENABLE_LOGS.
+ * @note If NRF_LWIP_DRIVER_CONFIG_LOG_ENABLED is disabled, having NRF_DRIVER_ENABLE_LOGS has no effect.
+ * @{
+ */
+
+#if NRF_LWIP_DRIVER_CONFIG_LOG_ENABLED
+
+#define NRF_LOG_MODULE_NAME LwIP
+
+#define NRF_LOG_LEVEL NRF_LWIP_DRIVER_CONFIG_LOG_LEVEL
+#define NRF_LOG_INFO_COLOR NRF_LWIP_DRIVER_CONFIG_INFO_COLOR
+#define NRF_LOG_DEBUG_COLOR NRF_LWIP_DRIVER_CONFIG_DEBUG_COLOR
+
+#include "nrf_log.h"
+NRF_LOG_MODULE_REGISTER();
+
+#define NRF_DRIVER_TRC NRF_LOG_DEBUG /**< Used for getting trace of execution in the module. */
+#define NRF_DRIVER_LOG NRF_LOG_INFO /**< Used for logging details. */
+#define NRF_DRIVER_ERR NRF_LOG_ERROR /**< Used for logging errors in the module. */
+#define NRF_DRIVER_DUMP NRF_LOG_HEXDUMP_DEBUG /**< Used for dumping octet information to get details of bond information etc. */
+
+#define NRF_DRIVER_ENTRY() NRF_DRIVER_TRC(">> %s", __func__)
+#define NRF_DRIVER_EXIT() NRF_DRIVER_TRC("<< %s", __func__)
+
+#else // NRF_LWIP_DRIVER_CONFIG_LOG_ENABLED
+
+#define NRF_DRIVER_TRC(...) /**< Disables traces. */
+#define NRF_DRIVER_LOG(...) /**< Disables loggs. */
+#define NRF_DRIVER_DUMP(...) /**< Disables dumping of octet streams. */
+#define NRF_DRIVER_ERR(...) /**< Disables error logs. */
+
+#define NRF_DRIVER_ENTRY(...)
+#define NRF_DRIVER_EXIT(...)
+
+#endif // NRF_LWIP_DRIVER_CONFIG_LOG_ENABLED
+
+/** Driver table to maintain mapping between IP interface and Bluetooth as transport. */
+struct blenetif {
+ iot_interface_t * p_ble_interface;
+ struct netif netif;
+};
+
+struct blenetif m_blenetif_table[BLE_6LOWPAN_MAX_INTERFACE]; /**< Table maintaining network interface of LwIP and also corresponding 6lowpan interface. */
+
+/** @brief Function to add received packet to pbuf queue and notify the stack of received packet. */
+static void blenetif_input(struct netif * p_netif, uint8_t * p_payload, uint16_t payload_len)
+{
+ struct pbuf * p_buffer;
+
+ NRF_DRIVER_ENTRY();
+ NRF_DRIVER_DUMP(p_payload, payload_len);
+
+ //Allocate a pbuf chain of pbufs from the pool.
+ p_buffer = pbuf_alloc(PBUF_RAW, payload_len, PBUF_REF);
+
+ if (p_buffer != NULL)
+ {
+ p_buffer->payload = p_payload;
+ p_buffer->len = payload_len;
+ p_buffer->tot_len = payload_len;
+ if (ip6_input(p_buffer, p_netif) != ERR_OK)
+ {
+ NRF_DRIVER_LOG("IP Stack returned error.");
+ }
+ UNUSED_VARIABLE(pbuf_free(p_buffer));
+ }
+
+ NRF_DRIVER_EXIT();
+}
+
+
+/** @brief Network interface output function registered with LwIP to send packets on 6lowpan. */
+static err_t blenetif_output(struct netif * p_netif, struct pbuf * p_buffer, const ip6_addr_t * p_addr)
+{
+ struct blenetif * p_blenetif = (struct blenetif *)p_netif->state;
+ uint8_t * p_payload;
+ err_t error_code = ERR_MEM;
+ const uint16_t requested_len = p_buffer->len;
+
+
+ NRF_DRIVER_ENTRY();
+ NRF_DRIVER_DUMP(p_buffer->payload, requested_len);
+
+ p_payload = nrf_malloc(p_buffer->len);
+
+ if (NULL != p_payload)
+ {
+ memcpy(p_payload, p_buffer->payload, p_buffer->len);
+ uint32_t retval = ble_6lowpan_interface_send(p_blenetif->p_ble_interface,
+ p_payload,
+ requested_len);
+ if (retval != NRF_SUCCESS)
+ {
+ NRF_DRIVER_ERR("Failed to send IP packet, reason 0x%08X", retval);
+ nrf_free(p_payload);
+ }
+ else
+ {
+ error_code = ERR_OK;
+ }
+ }
+ else
+ {
+ NRF_DRIVER_ERR("Failed to allocate memory for output packet");
+ }
+
+ NRF_DRIVER_EXIT();
+
+ return error_code;
+
+}
+
+
+/** @brief Initialization once network interface has been added. */
+static err_t blenetif_init(struct netif * p_netif)
+{
+ //Set link MTU size.
+ p_netif->mtu = BLE_IPSP_MTU;
+
+ //Device capabilities and link status
+ p_netif->flags = NETIF_FLAG_LINK_UP;
+
+ //Indicate interface link is up.
+ netif_set_up(p_netif);
+
+ //Enable Address Auto configuration.
+ p_netif->ip6_autoconfig_enabled = 1;
+
+ return ERR_OK;
+}
+
+
+/**@brief Callback registered with 6lowpan to receive asynchronous events. */
+static void blenetif_transport_callback(iot_interface_t * p_interface,
+ ble_6lowpan_event_t * p_event)
+{
+ uint32_t index;
+ uint32_t err_code;
+ struct blenetif * p_blenetif = NULL;
+
+ // Search network interface added for the 6lowpan interface that has generated the event.
+ for (index = 0; index < BLE_6LOWPAN_MAX_INTERFACE; index++)
+ {
+ if (p_interface == m_blenetif_table[index].p_ble_interface)
+ {
+ p_blenetif = &m_blenetif_table[index];
+ break;
+ }
+ }
+
+ switch(p_event->event_id)
+ {
+ case BLE_6LO_EVT_INTERFACE_ADD:
+ {
+ // If no existing interface was found, allocate a new one.
+ if (p_blenetif == NULL)
+ {
+ for (index = 0; index < BLE_6LOWPAN_MAX_INTERFACE; index++)
+ {
+ if (m_blenetif_table[index].p_ble_interface == NULL)
+ {
+ p_blenetif = &m_blenetif_table[index];
+ p_blenetif->p_ble_interface = p_interface;
+ break;
+ }
+ }
+ }
+ else
+ {
+ // Interface already exists, this is an error, should not happen.
+ break;
+ }
+
+ err_code = iot_context_manager_table_alloc(p_interface);
+
+ if (err_code == NRF_SUCCESS)
+ {
+ NRF_DRIVER_LOG("Successfully allocated context table!");
+ }
+ else
+ {
+ NRF_DRIVER_ERR("Failed to allocate context table!");
+ }
+
+#if (IPV6_LL_ADDR_SIZE == 6)
+ memcpy(p_blenetif->netif.hwaddr, p_interface->local_addr.identifier, 3);
+ memcpy(p_blenetif->netif.hwaddr + 3, p_interface->local_addr.identifier + 5, 3);
+#else
+ memcpy(p_blenetif->netif.hwaddr, p_interface->local_addr.identifier, IPV6_LL_ADDR_SIZE);
+#endif
+ p_blenetif->netif.hwaddr_len = IPV6_LL_ADDR_SIZE;
+
+ UNUSED_VARIABLE(netif_add(&p_blenetif->netif, p_blenetif, blenetif_init, NULL));
+
+ p_blenetif->netif.ip6_addr[0].addr[0] = HTONL(0xFE800000);
+ p_blenetif->netif.ip6_addr[0].addr[1] = HTONL(0x00000000);
+ p_blenetif->netif.ip6_addr[0].addr[2] = *((u32_t *) (&p_interface->local_addr.identifier[0]));
+ p_blenetif->netif.ip6_addr[0].addr[3] = *((u32_t *) (&p_interface->local_addr.identifier[4]));
+
+ // Flip local/universal bit.
+ p_blenetif->netif.ip6_addr[0].addr[2] ^= IPV6_IID_FLIP_VALUE;
+ p_blenetif->netif.ip6_addr_state[0] = IP6_ADDR_TENTATIVE;
+
+ p_blenetif->netif.output_ip6 = blenetif_output;
+
+ nrf_driver_interface_up(p_interface);
+
+ break;
+ }
+ case BLE_6LO_EVT_INTERFACE_DELETE:
+ {
+ if (p_blenetif != NULL)
+ {
+ netif_remove(&p_blenetif->netif);
+ p_blenetif->p_ble_interface = NULL;
+
+ err_code = iot_context_manager_table_free(p_interface);
+
+ if(err_code == NRF_SUCCESS)
+ {
+ NRF_DRIVER_ERR("Successfully freed context table!");
+ }
+ nrf_driver_interface_down(p_interface);
+ }
+ break;
+ }
+ case BLE_6LO_EVT_INTERFACE_DATA_RX:
+ {
+ if (p_blenetif != NULL)
+ {
+ // Handle received IP packet.
+ blenetif_input(&p_blenetif->netif,
+ p_event->event_param.rx_event_param.p_packet,
+ p_event->event_param.rx_event_param.packet_len);
+ nrf_free( p_event->event_param.rx_event_param.p_packet);
+ }
+ else
+ {
+ NRF_DRIVER_ERR("Dropping packet, unknown interface.");
+ }
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+
+/**@brief Driver initialization. */
+uint32_t nrf_driver_init(void)
+{
+ uint32_t err_code;
+ uint32_t index;
+ ble_6lowpan_init_t init_param;
+
+ init_param.event_handler = blenetif_transport_callback;
+ init_param.p_eui64 = EUI64_LOCAL_IID;
+
+ err_code = iot_context_manager_init();
+
+ if(err_code == NRF_SUCCESS)
+ {
+ /* 6lowpan initialization and callback registration. */
+ err_code = ble_6lowpan_init(&init_param);
+
+ for (index = 0; index < BLE_6LOWPAN_MAX_INTERFACE; index++)
+ {
+ m_blenetif_table[index].p_ble_interface = NULL;
+ }
+ }
+
+ return err_code;
+}
+
+
+/**@brief Message function to redirect LwIP debug traces to nRF tracing. */
+void nrf_message(const char * m)
+{
+ NRF_DRIVER_LOG(m);
+}
+
+
+/**@brief Interface implementation expected by the LwIP stack to get system ticks. */
+u32_t sys_now(void)
+{
+ u32_t sys_tick;
+
+ sys_tick = app_timer_cnt_get();
+
+ //Note: In case prescaler value is changed, this calculation to get sys tick applies.
+ //Currently, this is not used for saving in code execution as sys_now is called
+ //often.
+
+ //sys_tick = ROUNDED_DIV(((sys_tick & 0x00FFFFF) * (NRF_DRIVER_TIMER_PRESCALER + 1) * 1000), APP_TIMER_CLOCK_FREQ);
+ //sys_tick *= 8;
+ return sys_tick;
+}
+
+void mem_init(void)
+{
+}
+
+void *mem_trim(void *mem, mem_size_t size)
+{
+ return nrf_realloc(mem, size);
+}
+
+void *mem_malloc(mem_size_t size)
+{
+ return nrf_malloc(size);
+}
+
+void *mem_calloc(mem_size_t count, mem_size_t size)
+{
+ return nrf_calloc(count, size);
+}
+
+void mem_free(void *mem)
+{
+ nrf_free(mem);
+}
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/port/nrf_platform_port.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/port/nrf_platform_port.h
new file mode 100644
index 0000000..dfd1a82
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/port/nrf_platform_port.h
@@ -0,0 +1,47 @@
+/* Copyright (c) 2014 Nordic Semiconductor. All Rights Reserved.
+ *
+ * The information contained herein is property of Nordic Semiconductor ASA.
+ * Terms and conditions of usage are described in detail in NORDIC
+ * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT.
+ *
+ * Licensees are granted free, non-transferable use of the information. NO
+ * WARRANTY of ANY KIND is provided. This heading must NOT be removed from
+ * the file.
+ *
+ */
+
+/** @file nrf_driver.h
+ *
+ * @brief Implements lwip stack driver on nRF.
+ *
+ * @details Implements lwip stack driver on nRF.
+ */
+
+#ifndef NRF_PLATFORM_PORT_H__
+#define NRF_PLATFORM_PORT_H__
+
+#include "iot_common.h"
+
+/**@brief Prescaler value for timer module to get a tick of about 1 ms.
+ *
+ * @note Applications using lwIP and this driver, must use this value of prescaler when
+ * initializing the timer module.
+ */
+#define NRF_DRIVER_TIMER_PRESCALER 31
+
+/**@brief Initializes the driver for LwIP stack. */
+uint32_t nrf_driver_init(void);
+
+/**@brief API assumed to be implemented by the application to handle interface up event.
+ *
+ * @param[in] p_interface Identifies the interface.
+ */
+extern void nrf_driver_interface_up(iot_interface_t const * p_interface);
+
+/**@brief API assumed to be implemented by the application to handle interface down event.
+ *
+ * @param[in] p_interface Identifies the interface.
+ */
+extern void nrf_driver_interface_down(iot_interface_t const * p_interface);
+
+#endif //NRF_PLATFORM_PORT_H__